diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index 8d5417c99..385a05554 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -274,6 +274,24 @@ void Engine::RequestExit(int32 exitCode, FatalErrorType error) RequestingExit(); } +#if !BUILD_SHIPPING + +void Engine::Crash(FatalErrorType error) +{ + switch (error) + { + case FatalErrorType::None: + case FatalErrorType::Exception: + *((int32*)3) = 11; + break; + default: + Platform::Fatal(TEXT("Crash Test"), nullptr, error); + break; + } +} + +#endif + void Engine::OnFixedUpdate() { PROFILE_CPU_NAMED("Fixed Update"); diff --git a/Source/Engine/Engine/Engine.h b/Source/Engine/Engine/Engine.h index d0d095536..e8b6fef2c 100644 --- a/Source/Engine/Engine/Engine.h +++ b/Source/Engine/Engine/Engine.h @@ -125,6 +125,14 @@ public: /// The fatal error type (or None on graceful exit). API_FUNCTION() static void RequestExit(int32 exitCode = 0, FatalErrorType error = FatalErrorType::None); +#if !BUILD_SHIPPING + /// + /// Crashes the engine. Utility used to test crash reporting or game stability monitoring systems. + /// + /// The fatal error type. + API_FUNCTION(Attributes="DebugCommand") static void Crash(FatalErrorType error = FatalErrorType::Exception); +#endif + public: /// /// Fixed update callback used by the physics simulation (fixed stepping). diff --git a/Source/Engine/Level/Actors/Camera.cpp b/Source/Engine/Level/Actors/Camera.cpp index 6a54d88f3..9851b21b0 100644 --- a/Source/Engine/Level/Actors/Camera.cpp +++ b/Source/Engine/Level/Actors/Camera.cpp @@ -209,7 +209,7 @@ Ray Camera::ConvertMouseToRay(const Float2& mousePosition) const Ray Camera::ConvertMouseToRay(const Float2& mousePosition, const Viewport& viewport) const { Vector3 position = GetPosition(); - if (viewport.Width < ZeroTolerance || viewport.Height < ZeroTolerance) + if (viewport.Width < ZeroTolerance || viewport.Height < ZeroTolerance || mousePosition.IsNaN()) return Ray(position, GetDirection()); // Use different logic in orthographic projection diff --git a/Source/Engine/Platform/Apple/ApplePlatform.cpp b/Source/Engine/Platform/Apple/ApplePlatform.cpp index 8f4c12261..5104dd5f8 100644 --- a/Source/Engine/Platform/Apple/ApplePlatform.cpp +++ b/Source/Engine/Platform/Apple/ApplePlatform.cpp @@ -58,6 +58,43 @@ int32 AutoreleasePoolInterval = 0; float ApplePlatform::ScreenScale = 1.0f; +static void CrashHandler(int32 signal, siginfo_t* info, void* context) +{ + // Skip if engine already crashed + if (Engine::FatalError != FatalErrorType::None) + return; + + // Get exception info + String errorMsg(TEXT("Unhandled exception: ")); + switch (signal) + { +#define CASE(x) case x: errorMsg += TEXT(#x); break + CASE(SIGABRT); + CASE(SIGILL); + CASE(SIGSEGV); + CASE(SIGQUIT); + CASE(SIGEMT); + CASE(SIGFPE); + CASE(SIGBUS); + CASE(SIGSYS); +#undef CASE + default: + errorMsg += StringUtils::ToString(signal); + } + + // Log exception and return to the crash location when using debugger + if (Platform::IsDebuggerPresent()) + { + LOG_STR(Error, errorMsg); + const String stackTrace = Platform::GetStackTrace(3, 60, nullptr); + LOG_STR(Error, stackTrace); + return; + } + + // Crash engine + Platform::Fatal(errorMsg.Get(), nullptr, FatalErrorType::Exception); +} + String AppleUtils::ToString(CFStringRef str) { if (!str) @@ -90,41 +127,50 @@ NSString* AppleUtils::ToNSString(const char* string) return ret ? ret : @""; } - -NSArray* AppleUtils::ParseArguments(NSString* argsString) { - NSMutableArray *argsArray = [NSMutableArray array]; - NSMutableString *currentArg = [NSMutableString string]; +NSArray* AppleUtils::ParseArguments(NSString* argsString) +{ + NSMutableArray* argsArray = [NSMutableArray array]; + NSMutableString* currentArg = [NSMutableString string]; BOOL insideQuotes = NO; - - for (NSInteger i = 0; i < argsString.length; ++i) { + + for (NSInteger i = 0; i < argsString.length; i++) + { unichar c = [argsString characterAtIndex:i]; - - if (c == '\"') { - if (insideQuotes) { + if (c == '\"') + { + if (insideQuotes) + { [argsArray addObject:[currentArg copy]]; [currentArg setString:@""]; insideQuotes = NO; - } else { + } + else + { insideQuotes = YES; } - } else if (c == ' ' && !insideQuotes) { - if (currentArg.length > 0) { + } + else if (c == ' ' && !insideQuotes) + { + if (currentArg.length > 0) + { [argsArray addObject:[currentArg copy]]; [currentArg setString:@""]; } - } else { + } + else + { [currentArg appendFormat:@"%C", c]; } } - if (currentArg.length > 0) { + if (currentArg.length > 0) + { [argsArray addObject:[currentArg copy]]; } - + return [argsArray copy]; } - typedef uint16_t offset_t; #define align_mem_up(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) @@ -376,6 +422,21 @@ bool ApplePlatform::Init() setrlimit(RLIMIT_NOFILE, &limit); } + // Register crash handler + struct sigaction action; + Platform::MemoryClear(&action, sizeof(action)); + action.sa_sigaction = CrashHandler; + sigemptyset(&action.sa_mask); + action.sa_flags = SA_SIGINFO | SA_ONSTACK; + sigaction(SIGABRT, &action, nullptr); + sigaction(SIGILL, &action, nullptr); + sigaction(SIGSEGV, &action, nullptr); + sigaction(SIGQUIT, &action, nullptr); + sigaction(SIGEMT, &action, nullptr); + sigaction(SIGFPE, &action, nullptr); + sigaction(SIGBUS, &action, nullptr); + sigaction(SIGSYS, &action, nullptr); + AutoreleasePool = [[NSAutoreleasePool alloc] init]; return false;