18
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-14 13:25:56 +01:00
Files
maszyna/EU07.cpp
jerrrrycho 4356e7c3fd fix(simulation): a test run that can't start ends with the error code and says why
- `--test` is looked for before the rest of the command line is read. An
  unknown option before it (e.g. `-s x.scn --test-timout 90 --test`) used
  to end the run with exit code 0, which a caller reads as a pass.
- `--test-timeout` without a value, or with one that isn't a number of
  seconds, fails the run instead of leaving the default or, through atof,
  turning the timeout off.
- These, a missing -s, a failed start and an exception caught in main()
  end the test through test_manager::conclude(), which writes the usual
  `test: error (exit code 5) ... : <reason>` line to stdout and the log.
  Before, `eu07 --test` alone exited with 5 and nothing anywhere: the
  error went to the log queue and the process ended before the log
  thread wrote it. In test mode main() now stops the log thread and
  waits for it before _Exit().

Outside the test mode nothing changes: conclude() does nothing there.
2026-11-08 09:36:28 +01:00

194 lines
5.7 KiB
C++

/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
/*
MaSzyna EU07 locomotive simulator
Copyright (C) 2001-2004 Marcin Wozniak, Maciej Czapkiewicz and others
*/
/*
Authors:
MarcinW, McZapkie, Shaxbee, ABu, nbmx, youBy, Ra, winger, mamut, Q424,
Stele, firleju, szociu, hunter, ZiomalCl, OLI_EU and others
*/
#include "stdafx.h"
#include "application/application.h"
#include "simulation/simulationtest.h"
#include "utilities/Globals.h"
#include "utilities/Logs.h"
#include <cstdlib>
#include <format>
#ifdef WITHDUMPGEN
#ifdef _WIN32
#include <Windows.h>
#include <DbgHelp.h>
#endif
#endif
#ifdef _MSC_VER
#pragma comment(linker, "/subsystem:windows /ENTRY:mainCRTStartup")
#endif
void export_e3d_standalone(std::string in, std::string out, int flags, bool dynamic);
int export_btf_standalone(std::string in, std::string out);
int generate_nodebank_previews(int Argc, char *Argv[]);
#include <ctime>
#include <string>
#include <sstream>
#include <iomanip>
#include <utilities/Globals.h>
#ifdef _WIN32
#pragma comment(lib, "Dbghelp.lib")
LONG WINAPI CrashHandler(EXCEPTION_POINTERS *ExceptionInfo)
{
// Get current local time
SYSTEMTIME st;
GetLocalTime(&st);
// Format: crash_YYYY-MM-DD_HH-MM-SS.dmp
std::ostringstream oss;
oss << "crash_" << std::setw(4) << std::setfill('0') << st.wYear << "-" << std::setw(2) << std::setfill('0') << st.wMonth << "-" << std::setw(2) << std::setfill('0') << st.wDay << "_"
<< std::setw(2) << std::setfill('0') << st.wHour << "-" << std::setw(2) << std::setfill('0') << st.wMinute << "-" << std::setw(2) << std::setfill('0') << st.wSecond << ".dmp";
std::string filename = oss.str();
HANDLE hFile = CreateFileA(filename.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile != INVALID_HANDLE_VALUE)
{
MINIDUMP_EXCEPTION_INFORMATION dumpInfo;
dumpInfo.ThreadId = GetCurrentThreadId();
dumpInfo.ExceptionPointers = ExceptionInfo;
dumpInfo.ClientPointers = FALSE;
// Wybrana kombinacja flag
MINIDUMP_TYPE dumpType = MINIDUMP_TYPE(MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithThreadInfo | MiniDumpWithUnloadedModules | MiniDumpWithIndirectlyReferencedMemory |
MiniDumpWithFullMemoryInfo | MiniDumpWithTokenInformation);
MessageBoxA(nullptr, "Simulator crash occured :(\n", "Simulator crashed :(", MB_ICONERROR);
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, dumpType, &dumpInfo, nullptr, nullptr);
CloseHandle(hFile);
}
return EXCEPTION_EXECUTE_HANDLER;
}
#endif
namespace
{
// the log is written by a thread of its own, which the closing of the application stops and waits for. on the ways
// out that skip the closing (a failed start, an exception) what was logged last would be lost with the process
void finish_log()
{
StopLogService();
if (auto const logservice{Global.threads.find("LogService")}; logservice != Global.threads.end() && logservice->second.joinable())
logservice->second.join();
}
} // namespace
int main(int argc, char *argv[])
{
#ifdef WITHDUMPGEN
#ifdef _WIN32
SetUnhandledExceptionFilter(CrashHandler);
#endif
#endif
// init start timestamp
Global.startTimestamp = std::chrono::steady_clock::now();
// quick short-circuit for standalone e3d export
if (argc == 6 && std::string(argv[1]) == "-e3d")
{
std::string in(argv[2]);
std::string out(argv[3]);
int flags = std::stoi(std::string(argv[4]));
int dynamic = std::stoi(std::string(argv[5]));
export_e3d_standalone(in, out, flags, dynamic);
}
// standalone generation of preview images of the node bank models: -generate-nodebank-previews [options]
else if (argc >= 2 && std::string(argv[1]) == "-generate-nodebank-previews")
{
auto const result{generate_nodebank_previews(argc, argv)};
fflush(stdout);
fflush(stderr);
std::_Exit(result);
}
// standalone conversion of text terrain file to binary format: -btf -s <text file> [-o <binary file>]
else if (argc >= 4 && std::string(argv[1]) == "-btf")
{
std::string in;
std::string out;
for (int i = 2; i + 1 < argc; i += 2)
{
std::string const option(argv[i]);
if (option == "-s")
{
in = argv[i + 1];
}
else if (option == "-o")
{
out = argv[i + 1];
}
}
auto const result{export_btf_standalone(in, out)};
fflush(stdout);
fflush(stderr);
std::_Exit(result);
}
else
{
try
{
auto result{Application.init(argc, argv)};
if (result == 0)
{
result = Application.run();
Application.exit();
}
}
catch (std::bad_alloc const &Error)
{
ErrorLog("Critical error, memory allocation failure: " + std::string(Error.what()));
}
#ifdef _WIN32
catch (std::runtime_error const &Error)
{
ErrorLog(std::format("Critical error: {}", Error.what()));
// in test mode nobody is there to close the box, the exit code tells of the error
simulation::test().conclude(simulation::test_result::error, std::format("critical error: {}", Error.what()));
if (false == Global.test.enabled)
{
std::string msg = "Simulator crash occured :(\n";
msg += Error.what();
MessageBoxA(nullptr, msg.c_str(), "Simulator crashed :(", MB_ICONERROR);
}
}
#endif
}
#ifndef _WIN32
fflush(stdout);
fflush(stderr);
#endif
if (Global.test.enabled)
{
// a run that stopped before its test ended (a failed start, an exception) says so like any other end
simulation::test().conclude(simulation::test_result::error, "the simulator stopped before the test ended");
finish_log();
}
// skip destructors, there are ordering errors which causes segfaults
// a test run (--test) says its result in the exit code
std::_Exit(Global.test.enabled ? simulation::test().exit_code() : 0);
}