2015-09-03 20:46:02 -04:00
// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
# include "LinuxCommonStartup.h"
# include "ExceptionHandling.h"
# include "LinuxPlatformCrashContext.h"
# include "ModuleManager.h"
2015-09-04 09:48:52 -04:00
# if UE_BUILD_SHIPPING
# include "EngineVersion.h"
# endif // UE_BUILD_SHIPPING
2015-09-03 20:46:02 -04:00
# include <locale.h>
# include <sys/resource.h>
static FString GSavedCommandLine ;
extern int32 GuardedMain ( const TCHAR * CmdLine ) ;
extern void LaunchStaticShutdownAfterError ( ) ;
// FIXME: handle expose it someplace else?
extern int32 DLLIMPORT ReportCrash ( const FLinuxCrashContext & Context ) ;
extern void DLLIMPORT GenerateCrashInfoAndLaunchReporter ( const FLinuxCrashContext & Context ) ;
/**
* Game - specific crash reporter
*/
void CommonLinuxCrashHandler ( const FGenericCrashContext & GenericContext )
{
const FLinuxCrashContext & Context = static_cast < const FLinuxCrashContext & > ( GenericContext ) ;
printf ( " EngineCrashHandler: Signal=%d \n " , Context . Signal ) ;
ReportCrash ( Context ) ;
if ( GLog )
{
GLog - > Flush ( ) ;
}
if ( GWarn )
{
GWarn - > Flush ( ) ;
}
if ( GError )
{
GError - > Flush ( ) ;
GError - > HandleError ( ) ;
}
return GenerateCrashInfoAndLaunchReporter ( Context ) ;
}
/**
* Increases ( soft ) limit on a specific resource
*
* @ param DesiredLimit - max number of open files desired .
*/
static bool IncreaseLimit ( int Resource , rlim_t DesiredLimit )
{
rlimit Limit ;
if ( getrlimit ( Resource , & Limit ) ! = 0 )
{
fprintf ( stderr , " getrlimit() failed with error %d (%s) \n " , errno , strerror ( errno ) ) ;
return false ;
}
if ( Limit . rlim_cur = = RLIM_INFINITY | | Limit . rlim_cur > = DesiredLimit )
{
# if !UE_BUILD_SHIPPING
printf ( " - Existing per-process limit (soft=%lu, hard=%lu) is enough for us (need only %lu) \n " , Limit . rlim_cur , Limit . rlim_max , DesiredLimit ) ;
# endif // !UE_BUILD_SHIPPING
return true ;
}
Limit . rlim_cur = DesiredLimit ;
if ( setrlimit ( Resource , & Limit ) ! = 0 )
{
fprintf ( stderr , " setrlimit() failed with error %d (%s) \n " , errno , strerror ( errno ) ) ;
if ( errno = = EINVAL )
{
if ( DesiredLimit = = RLIM_INFINITY )
{
fprintf ( stderr , " - Max per-process value allowed is %lu (we wanted infinity). \n " , Limit . rlim_max ) ;
}
else
{
fprintf ( stderr , " - Max per-process value allowed is %lu (we wanted %lu). \n " , Limit . rlim_max , DesiredLimit ) ;
}
}
return false ;
}
return true ;
}
/**
* Expects GSavedCommandLine to be set up . Increases limit on
* - number of open files to be no less than desired ( if specified on command line , otherwise left alone )
* - size of core file , so core gets dumped and we can debug crashed builds ( unless overridden with - nocore )
*
*/
static bool IncreasePerProcessLimits ( )
{
// honor the parameter if given, but don't change limits if not
int32 FileHandlesToReserve = - 1 ;
if ( FParse : : Value ( * GSavedCommandLine , TEXT ( " numopenfiles= " ) , FileHandlesToReserve ) & & FileHandlesToReserve > 0 )
{
# if !UE_BUILD_SHIPPING
printf ( " Increasing per-process limit of open file handles to %d \n " , FileHandlesToReserve ) ;
# endif // !UE_BUILD_SHIPPING
if ( ! IncreaseLimit ( RLIMIT_NOFILE , FileHandlesToReserve ) )
{
fprintf ( stderr , " Could not adjust number of file handles, consider changing \" nofile \" in /etc/security/limits.conf and relogin. \n error(%d): %s \n " , errno , strerror ( errno ) ) ;
return false ;
}
}
# if !UE_BUILD_SHIPPING
if ( ! FParse : : Param ( * GSavedCommandLine , TEXT ( " nocore " ) ) )
{
printf ( " Increasing per-process limit of core file size to infinity. \n " ) ;
if ( ! IncreaseLimit ( RLIMIT_CORE , RLIM_INFINITY ) )
{
fprintf ( stderr , " Could not adjust core file size, consider changing \" core \" in /etc/security/limits.conf and relogin. \n error(%d): %s \n " , errno , strerror ( errno ) ) ;
fprintf ( stderr , " Alternatively, pass -nocore if you are unable or unwilling to do that. \n " ) ;
return false ;
}
}
# endif // !UE_BUILD_SHIPPING
return true ;
}
int CommonLinuxMain ( int argc , char * argv [ ] , int ( * RealMain ) ( const TCHAR * CommandLine ) )
{
FPlatformMisc : : SetGracefulTerminationHandler ( ) ;
# if UE_BUILD_SHIPPING
// only printed in shipping
printf ( " %s %d %d %d %d \n " , StringCast < ANSICHAR > ( * FEngineVersion : : Current ( ) . ToString ( ) ) . Get ( ) , GEngineMinNetVersion , GEngineNegotiationVersion , GPackageFileUE4Version , GPackageFileLicenseeUE4Version ) ;
# endif // UE_BUILD_SHIPPING
int ErrorLevel = 0 ;
setlocale ( LC_CTYPE , " " ) ;
for ( int32 Option = 1 ; Option < argc ; Option + + )
{
GSavedCommandLine + = TEXT ( " " ) ;
// we need to quote stuff that has spaces in it because something somewhere is removing quotation marks before they arrive here
FString Temp = UTF8_TO_TCHAR ( argv [ Option ] ) ;
if ( Temp . Contains ( TEXT ( " " ) ) )
{
if ( Temp . StartsWith ( TEXT ( " - " ) ) )
{
Temp = Temp . Replace ( TEXT ( " = " ) , TEXT ( " = \" " ) ) ;
}
else
{
Temp = TEXT ( " \" " ) + Temp ;
}
Temp + = TEXT ( " \" " ) ;
}
GSavedCommandLine + = Temp ; // note: technically it depends on locale
}
# if !UE_BUILD_SHIPPING
GAlwaysReportCrash = true ; // set by default and reverse the behavior
if ( FParse : : Param ( * GSavedCommandLine , TEXT ( " nocrashreports " ) ) | | FParse : : Param ( * GSavedCommandLine , TEXT ( " no-crashreports " ) ) )
{
GAlwaysReportCrash = false ;
}
# endif
if ( ! IncreasePerProcessLimits ( ) )
{
fprintf ( stderr , " Could not set desired per-process limits, consider changing system limits. \n " ) ;
ErrorLevel = 1 ;
}
else
{
# if UE_BUILD_DEBUG
if ( true & & ! GAlwaysReportCrash )
# else
if ( FPlatformMisc : : IsDebuggerPresent ( ) & & ! GAlwaysReportCrash )
# endif
{
// Don't use exception handling when a debugger is attached to exactly trap the crash. This does NOT check
// whether we are the first instance or not!
ErrorLevel = RealMain ( * GSavedCommandLine ) ;
}
else
{
FPlatformMisc : : SetCrashHandler ( CommonLinuxCrashHandler ) ;
GIsGuarded = 1 ;
// Run the guarded code.
ErrorLevel = RealMain ( * GSavedCommandLine ) ;
GIsGuarded = 0 ;
}
}
if ( ErrorLevel )
{
printf ( " Exiting abnormally (error code: %d) \n " , ErrorLevel ) ;
}
return ErrorLevel ;
}
class FLinuxCommonStartupModule : public IModuleInterface
{
/** IModuleInterface implementation */
virtual void StartupModule ( ) override { } ;
virtual void ShutdownModule ( ) override { } ;
} ;
IMPLEMENT_MODULE ( FLinuxCommonStartupModule , LinuxCommonStartup ) ;