Search Unity

IL2CPP - Fast but no Exceptions vs. Slow and Safe

Discussion in 'iOS and tvOS' started by TactileGames, Oct 7, 2015.

  1. TactileGames

    TactileGames

    Joined:
    Oct 6, 2014
    Posts:
    9
    Hi,

    Is there any difference in the IL2CPP generated code when setting Script Call Optimization to "Fast but no Exceptions" vs. when it is set to "Slow and Safe" ?

    We tried doing a directory comparison of the generated code for our game and it did not show any difference (except for a timestamp in a comment). So obviously that indicates that there is no difference, but it would be good to get it confirmed, since the accepted "rule" for the Mono 2.x backend was that you should always run with "Fast but no Exceptions".

    We are aware of the difference introduced in 4.6.7p2 whereby the old behaviour of crashing the engine in case of an unhandled exception was introduced for IL2CPP code (as it should be). Is this actually the only difference between Fast but no Exceptions vs Slow and Safe when using IL2CPP ?

    If it is the case that there is no difference in the code generated, then it must also be the case that there is no longer any constant overhead of using exceptions within an iOS project - currently the documentation for iOS player settings (http://docs.unity3d.com/Manual/class-PlayerSettingsiOS.html) indicates that you should not use any exceptions on iOS - maybe this statement needs a bit more nuance when using the IL2CPP backend ?

    I hope somebody at Unity would be kind enough to clear this up.

    Best regards,
    Morten Nielsen
     
  2. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,936
    @TactileEntertainment

    Yes, this is a bit of a confusing setting, sorry about that. It is in our backlog to be updated and improved (including the documentation) for IL2CPP, so stay tuned.

    Yes, this is the only difference between the two settings with IL2CPP. So if you want or need the unhandled exception behavior, they use Fast but no Exceptions.
     
  3. TactileGames

    TactileGames

    Joined:
    Oct 6, 2014
    Posts:
    9
    @JoshPeterson

    Thanks for confirming. Now to the real problem and why we are concerned about this :)

    We sometimes experience unhandled exceptions where we receive an empty StackTrace on iOS (using Unity 4.6.6p4). Our exception handler looks like this:

    Code (CSharp):
    1. public void OnHandleUnresolvedException(object sender, System.UnhandledExceptionEventArgs args){
    2.     if(args == null || args.ExceptionObject == null)
    3.     {
    4.         return;
    5.     }
    6.  
    7.     if(args.ExceptionObject.GetType() == typeof(System.Exception))
    8.     {
    9.         System.Exception e    = (System.Exception)args.ExceptionObject;
    10.         HandleException(e.Source, e.StackTrace); //Write the stack trace to disk and try to send it to HockeyApp
    11.     }
    12. }
    On iOS, e.StackTrace is sometimes empty. We never see this on Android, which uses the exact same code. We are not able to reproduce these empty stack traces ourselves, but we receive a fair amount every day in our HockeyApp account.

    To try and work around this, we started investigating not installing an unhandled exception handler (like in the pre-IL2CPP days) on iOS and instead rely on the native stack traces. Unfortunately the produced stack traces are not useful, since they are missing the most critical part of the stack. This was done using Unity 4.6.8p3.

    An example should illustrate the problem. This is is the stacktrace where the exception is raised inside NullCheck:


    This is the stacktrace we are left with when we reach CrashedCheckBellowForHintsWhy:


    As you can see the critical part of the stack is missing; DeveloperView_CrashNullReferenceException_m5366 (and NullCheck).

    It would be good to get your opinion on the empty stack traces received when using the C# unhandled exception handler on iOS. Does it even make sense for us to open a bug report on this, seeing as we have no way of reproducing it?

    With regards to the native crashes - these would be preferable in the long run if a solution could be found to produce stacktraces that include the source of raising the exception. Normally the full stack should be available when calling abort(), but maybe the exception is caught and rethrown by the engine causing us to loose precious information. Does it make sense to report a bug / feature request for this?

    Best regards,
    Morten Nielsen
     
  4. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,936
    @TactileEntertainment

    We did have a bug in 4.6.6p4 (and earlier versions), which could lead to an empty stack trace on armv7 (32-bit) devices only. It sounds very similar to what you are seeing, as it was intermittent. This bug was corrected in 4.6.7p1 (and later). Is it possible for you to determine whether or not all of these crashes with empty stack traces were on 32-bit devices? If so, then I think we've found the culprit here. If some 64-bit devices exhibit this missing stack trace, then we'll have more to track down.

    I believe that abort() will give you the full native call stack, and the engine code should not be doing anything to catch an abort() with the IL2CPP scripting backend. I've added this possibility to our backlog of things to look at when we revisit this exception handling mechanism (as I mentioned above).
     
  5. TactileGames

    TactileGames

    Joined:
    Oct 6, 2014
    Posts:
    9
    Yes we do have that information available and yes they are all 32 bit devices. We will upgrade to the latest 4.6.8 patch release for our next submissions.

    With regards to the native crash callstack, then I am almost certain that it does not provide the full callstack. We will create a test project and file a bug report.

    Thanks a lot for your speedy replies and the great insight!

    Best,
    Morten
     
  6. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,936
    @TactileEntertainment

    I've spent some time looking at this recently, and I don't think it is possible to emit an abort when a managed exception occurs when "Fast but no exceptions" is enabled.

    For iOS, we use the unhandled exception handler to get input for the crash reporting API (http://docs.unity3d.com/ScriptReference/CrashReport.html). So we can't abort, but instead we need the runtime to call the unhandled exception handler. That is what you are seeing in the first call stack screen shot above.

    However, you should be able to get a proper managed stack trace. Were you able to get that to work by using a new version of Unity?
     
  7. TactileGames

    TactileGames

    Joined:
    Oct 6, 2014
    Posts:
    9
    Hi Josh,

    Yes we were able to get that working correctly with the latest 4.6.x version of Unity, so that solved it for us.

    Best,
    Morten
     
  8. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,936
    @TactileEntertainment

    Excellent! Thanks for letting me know.
     
  9. nbaris

    nbaris

    Joined:
    Jan 13, 2015
    Posts:
    27
    Hi Josh,

    Are you talking about unhandled exceptions in managed code here?

    The manual suggests to use "fast but no exceptions" and never to use try/catch for iOS; but are handled exceptions in managed code meant to work normally with IL2CPP, even with "fast but no exceptions"?

    Thanks
     
  10. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,936
    @nbaris

    > Are you talking about unhandled exceptions in managed code here?

    Yes, I am talking about unhandled exceptions in managed code.

    > are handled exceptions in managed code meant to work normally with IL2CPP, even with "fast but no exceptions"?

    Yes, managed exceptions with try/catch blocks generally work as you would expect for a .NET runtime with the IL2CPP scripting backend. Some platforms (e.g. WebGL) can disable exceptions entirely via a different settings, but that is not the case on iOS.

    For iOS with the IL2CPP scripting backend, "fast but no exceptions" simply means that if a managed exception escapes from user script code back to the engine, the engine code will call AppDomain.UnhandledExceptionHandler to report the exception. On iOS the default behavior of the unhandled exception handler is to log the exception in formation in the CrashReport feature, then abort.
     
  11. nbaris

    nbaris

    Joined:
    Jan 13, 2015
    Posts:
    27
    Thanks, this is very helpful!

    Just out of curiosity.. Before IL2CPP, this option affected exceptions in the native code as well, right?
     
  12. JoshPeterson

    JoshPeterson

    Unity Technologies

    Joined:
    Jul 21, 2014
    Posts:
    6,936
    @nbaris

    I don't believe that settings has an impact on native code with the Mono scripting backend. With Mono, it does change the way that native -> managed calls occur. The "fast but no exceptions" setting means those calls are made directly, without an intervening wrapper function which handles a managed exception that might occur.