Add a self-contained, thread-safe crash/error log (common/CrashLog) that
survives process death, plus instrumentation at key lifecycle points so a
crash log reveals the system state that led to it.
- CrashLog.{h,cpp}: append-only eve.log recorder with initSystemLogging,
recordLogEvent, recordCrashEvent (try-lock safe for crash handlers) and
crashLogPath. EVE_LOG_DIR overrides the location.
- CrashHandler.h: unhandled-exception backtrace is now also written to
eve.log, not only stderr.
- main.cpp: open the log before anything can fail.
- Run.cpp: record run start + top-level failures; bind eve.log() so scripts
share the same crash log.
- vulkan/Graphics.cpp: log GPU identity/driver/caps and graphics init
milestones (swapchain/pipelines) - the likeliest hard-crash area.
- load.nut: boot milestones (modules, window+vulkan, game script) and a
periodic frame-count marker to locate how far the loop got.
- test/crashlog.cpp: init/record/crash coverage.
105 lines
3.7 KiB
C++
105 lines
3.7 KiB
C++
#include "common/config.h"
|
|
#include "common/CrashHandler.h"
|
|
#include "common/CrashLog.h"
|
|
#include "cmdline/cmdline.h"
|
|
#include <CLI11.hpp>
|
|
#include <rang.hpp>
|
|
#include <chrono>
|
|
#include <cstdio>
|
|
#include <ctime>
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#if defined(EVENGINE_ANDROID) || defined(EVENGINE_IOS) || defined(EVENGINE_WEBGPU)
|
|
#include <SDL2/SDL.h>
|
|
#include <SDL2/SDL_main.h>
|
|
#endif
|
|
|
|
#if defined(EVENGINE_IOS)
|
|
#include "ios/ios.h"
|
|
#endif
|
|
|
|
#if defined(EVENGINE_WEBGPU)
|
|
#include "webgpu/webplatform.h"
|
|
#endif
|
|
|
|
using namespace eve;
|
|
using namespace std;
|
|
|
|
namespace {
|
|
// Earliest code we control: runs during static initialization, before main().
|
|
const auto gEveStaticInitStart = std::chrono::steady_clock::now();
|
|
} // namespace
|
|
|
|
class MyFormatter : public CLI::Formatter {
|
|
public:
|
|
std::string make_usage(const CLI::App *app, std::string name) const override {
|
|
std::string usage = "Usage: ";
|
|
usage += name;
|
|
usage += " [Options] [Root Folder]";
|
|
return usage;
|
|
}
|
|
};
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
// Make script print() / cout diagnostics visible immediately when stdout is
|
|
// redirected (CI logs, pipes): the C runtime fully buffers stdout when it
|
|
// is not a console, so a startup marker from eve_init could otherwise stay
|
|
// in the buffer and be lost when the process is killed after a smoke run.
|
|
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
|
std::cout.setf(std::ios::unitbuf);
|
|
|
|
// Open the crash/error log before anything that could fail, so hard crashes
|
|
// and uncaught exceptions are persisted (see common/CrashLog.h).
|
|
eve::initSystemLogging();
|
|
|
|
#if defined(EVENGINE_WINDOWS) || defined(_WIN32)
|
|
eve::installCrashHandler();
|
|
// Diagnostic hook: EVE_TEST_CRASH=1 forces an access violation right after
|
|
// startup so the crash handler output can be verified.
|
|
const char *testCrash = std::getenv("EVE_TEST_CRASH");
|
|
if (testCrash && testCrash[0] != '\0' && testCrash[0] != '0') {
|
|
std::fprintf(stderr, "[crash] EVE_TEST_CRASH: forcing an access violation\n");
|
|
*static_cast<volatile int *>(nullptr) = 0;
|
|
}
|
|
#endif
|
|
const auto tMain = std::chrono::steady_clock::now();
|
|
std::fprintf(stderr, "[startup] static-init -> main(): %.1f ms\n",
|
|
std::chrono::duration<double, std::milli>(tMain - gEveStaticInitStart).count());
|
|
std::fprintf(stderr, "[startup] process clock() at main(): %.1f ms\n",
|
|
(double) std::clock() * 1000.0 / (double) CLOCKS_PER_SEC);
|
|
#if defined(EVENGINE_IOS)
|
|
// UIKit launches with no CLI args; inject `run <bundle game dir>` like Android Activity.
|
|
// Qualify eve::ios to avoid ambiguity with std::ios after using-directives.
|
|
static std::string gamePath;
|
|
static std::vector<char *> injected;
|
|
if (argc <= 1) {
|
|
gamePath = eve::ios::getGameDirectory();
|
|
if (gamePath.empty())
|
|
gamePath = ".";
|
|
static char runFlag[] = "run";
|
|
injected = {argv[0], runFlag, gamePath.data()};
|
|
argc = static_cast<int>(injected.size());
|
|
argv = injected.data();
|
|
}
|
|
eve::ios::initAudioSessionInterruptionHandler();
|
|
#elif defined(EVENGINE_WEBGPU)
|
|
// The browser launches with no CLI args; inject `run <game root>` where
|
|
// the game is the preloaded VFS mount (/game) or the CWD.
|
|
static std::string gamePath;
|
|
static std::vector<char *> injected;
|
|
if (argc <= 1) {
|
|
gamePath = eve::webgpu_platform::getGameDirectory();
|
|
if (gamePath.empty())
|
|
gamePath = ".";
|
|
static char runFlag[] = "run";
|
|
injected = {argv[0], runFlag, gamePath.data()};
|
|
argc = static_cast<int>(injected.size());
|
|
argv = injected.data();
|
|
}
|
|
#endif
|
|
return requireModInst(eve::cmd,Cmdline)->runArgs(argc, argv);
|
|
}
|