Bug 42778 - SIGABRT / SI_TKILL when using Newtonsoft.Json with a simple F# Enum
Summary: SIGABRT / SI_TKILL when using Newtonsoft.Json with a simple F# Enum
Alias: None
Product: Android
Classification: Xamarin
Component: Mono runtime / AOT Compiler ()
Version: 6.1.0 (C7)
Hardware: PC Mac OS
: --- normal
Target Milestone: 7.0 (C8)
Assignee: Jonathan Pryor
Depends on:
Reported: 2016-07-25 09:19 UTC by Nathan B. Evans
Modified: 2016-08-01 18:20 UTC (History)
2 users (show)

Is this bug a regression?: ---
Last known good build:

F# and C# projects to demo/repro the bug (25.17 KB, application/zip)
2016-07-25 09:19 UTC, Nathan B. Evans
Scratch.ConsoleBxc42778.zip (3.83 KB, application/zip)
2016-07-25 20:09 UTC, Jonathan Pryor

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description Nathan B. Evans 2016-07-25 09:19:13 UTC
Created attachment 16776 [details]
F# and C# projects to demo/repro the bug

This took me almost 8 hours to figure out what was causing the hard crash in our massive app. It appears there has been a huge regression somewhere in the depths of the Mono runtime such that now simple F# data types when used with Newtonsoft.Json can completely "tombstone" your app as follows:

> 		I/DEBUG   (13263): signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
> 		I/DEBUG   (13263): Abort message: '* Assertion: should not be reached at
> 		/Users/builder/data/lanes/3415/7db2aac3/source/mono/mono/metadata/icall.c:3042

* Confirmed the crash on Xamarin.Android
* Confirmed the correct behavior on Xamarin.Android 4.20.2
* Not tested on any versions between the above range.
* Start blank F# Android project and paste this as your MainActivity.fs:

> 		namespace App2
> 		open Android.App
> 		open Newtonsoft.Json
> 		type FontAwesomeLiteralTest =
> 		| gbp = 'A'
> 		| bar = 'B'
> 		[<Activity (Label = "App2", MainLauncher = true)>]
> 		type MainActivity () =
> 		    inherit Activity ()
> 		    let json = "\"gbp\""
> 		    override this.OnCreate (bundle) =
> 		        base.OnCreate (bundle)
> 		        this.SetContentView (Resource_Layout.Main)
> 		        JsonConvert.DeserializeObject<string>(json) |> ignore // doesn't tombstone
> 		        JsonConvert.DeserializeObject<FontAwesomeLiteralTest>(json) |> ignore // tombstones! argh

* For bonus points I made an identical C# Android project with the following code:

> 		using Android.App;
> 		using Android.OS;
> 		using Newtonsoft.Json;
> 		enum FontAwesomeLiteralTest { gbp = 'A', bar = 'B' }
> 		namespace App1
> 		{
> 		    [Activity(Label = "App1", MainLauncher = true, Icon = "@drawable/icon")]
> 		    public class MainActivity : Activity
> 		    {
> 		        string json = "\"gbp\"";
> 		        protected override void OnCreate(Bundle bundle)
> 		        {
> 		            base.OnCreate(bundle);
> 		            SetContentView(Resource.Layout.Main);
> 		            JsonConvert.DeserializeObject<string>(json); // doesn't tombstone
> 		            JsonConvert.DeserializeObject<FontAwesomeLiteralTest>(json); // tombstones! argh
> 		        }
> 		    }
> 		}

* This C# app does NOT tombstone/crash. So this pinpoints a Mono regression that affects F# specifically.

* Attached a ZIP with .csproj, .fsproj, .sln and associated code files.
Comment 1 Jonathan Pryor 2016-07-25 20:09:15 UTC
Created attachment 16779 [details]
Comment 2 Jonathan Pryor 2016-07-25 20:11:02 UTC
See Attachment #16779 [details], which is a repro *without a Xamarin.Android dependency*. (Yay.)

> $ unzip Scratch.ConsoleBxc42778.zip
> $ cd Scratch.ConsoleBxc42778
> $ nuget restore
> $ xbuild
> $ mono Scratch.ConsoleBxc42778/bin/Debug/Scratch.ConsoleBxc42778.exe


* Assertion: should not be reached at icall.c:3042
> Stacktrace:
>   at <unknown> <0xffffffff>
>   at (wrapper managed-to-native) System.Enum.InternalBoxEnum (System.RuntimeType,long) <0x00012>
>   at System.Enum.ToObject (System.Type,ulong) <0x0008b>
>   at System.Enum.TryParseEnum (System.Type,string,bool,System.Enum/EnumResult&) <0x00522>
>   at System.Enum.Parse (System.Type,string,bool) <0x000a8>
>   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.EnsureType (Newtonsoft.Json.JsonReader,object,System.Globalization.CultureInfo,Newtonsoft.Json.Serialization.JsonContract,System.Type) <0x0010f>
>   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal (Newtonsoft.Json.JsonReader,System.Type,Newtonsoft.Json.Serialization.JsonContract,Newtonsoft.Json.Serialization.JsonProperty,Newtonsoft.Json.Serialization.JsonContainerContract,Newtonsoft.Json.Serialization.JsonProperty,object) <0x001a7>
>   at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize (Newtonsoft.Json.JsonReader,System.Type,bool) <0x001c3>
>   at Newtonsoft.Json.JsonSerializer.DeserializeInternal (Newtonsoft.Json.JsonReader,System.Type) <0x0017b>
>   at Newtonsoft.Json.JsonSerializer.Deserialize (Newtonsoft.Json.JsonReader,System.Type) <0x0001e>
>   at Newtonsoft.Json.JsonConvert.DeserializeObject (string,System.Type,Newtonsoft.Json.JsonSerializerSettings) <0x000bf>
>   at Newtonsoft.Json.JsonConvert.DeserializeObject<Program/FontAwesomeLiteralTest> (string,Newtonsoft.Json.JsonSerializerSettings) <0x0001f>
>   at Newtonsoft.Json.JsonConvert.DeserializeObject<Program/FontAwesomeLiteralTest> (string) <0x0001b>
>   at Program.main (string[]) <0x0004b>
>   at (wrapper runtime-invoke) <Module>.runtime_invoke_int_object (object,intptr,intptr,intptr) <0x000c3>
Comment 3 Nathan B. Evans 2016-07-25 20:32:54 UTC
So a Mono regression then?
Comment 4 Nathan B. Evans 2016-07-25 21:59:53 UTC
>   open System
>   type FontAwesomeLiteralTest =
>   | gbp = 'A'
>   | bar = 'B'
>   [<EntryPoint>]
>   let main argv = 
>       let r =
>          match Enum.TryParse<FontAwesomeLiteralTest>("gbp", ignoreCase = false) with
>          | true, v -> box v | false, _ -> invalidOp "Oh."
>       printfn "%A" r
>       0
Comment 5 Nathan B. Evans 2016-07-25 22:00:49 UTC
See above for revised repro. Completely nukes the Mono runtime.
Comment 6 Nathan B. Evans 2016-07-25 22:12:05 UTC
Another repro, alternate:

>   open System
>   type FontAwesomeLiteralTest =
>   | gbp = 'A'
>   | bar = 'B'
>   [<EntryPoint>]
>   let main argv = 
>       let r = Enum.GetValues(typeof<FontAwesomeLiteralTest>)
>       printfn "%A" r
>       0

Just use Enum.GetValues on your enum and BOOM. Get that same exception from deep in Mono's icall.c
Comment 7 Nathan B. Evans 2016-07-25 22:15:35 UTC

Apparently it has been fixed way back in April? Not reached stable yet?

Is there a beta or alpha of Xamarin.Android I can use?
Comment 9 Jonathan Pryor 2016-07-25 23:04:05 UTC
The current Xamarin.Android "6.1.99" (to be 6.2.0) alpha is based on Mono 4.6, which includes that commit, so *in theory* your app should work on the current alpha.

In practice, the last "alpha" was more of a "quick, what do we have on master on date X!" and effectively no QA to speak of. If it doesn't blow up, that would be surprising.

There should be a new alpha coming out soon...with marginally more QA, but *this* time we're trying to stabilize for an eventual stable release, unlike last time, which was a "what can we release for Evolve?"
Comment 10 Nathan B. Evans 2016-07-26 07:08:11 UTC
I'll try the Alpha for understanding - but obviously can't use this for our production app.

I am confused why the C# version of the Enum works but the F# version doesn't. Perhaps internally C# enums of type "char" do not actually use the runtime's char enum type (MONO_TYPE_CHAR)? Do C# char enums compile down to a 16-bit integer instead (MONO_TYPE_U2 or MONO_TYPE_I2)?
Comment 11 Nathan B. Evans 2016-07-26 07:53:32 UTC
Turns out C# Char Enums use an int16 as the underlying type - I've been away from C#-land for so long that I had forgot this!

Whereas F# "does it properly" by using an actual Char type as the underlying type.

I'm checking to see if there a possible solution where I define my Char Enum using hex integers instead and then a JsonConverter to handle the conversion logic where needed. Seems a better approach than waiting probably at least 6 months for this Mono patch to propagate to Stable (or even Beta) Xamarin.
Comment 12 Nathan B. Evans 2016-07-26 10:07:10 UTC
To confirm the F# Char Enum is still broken on the latest Alpha of Xamarin Studio.

I implemented a workaround that basically stops using Char Enum and uses Integer Enum instead, with a couple other fixups to handle the conversion and this seems to be working fine. I think this is the best solution for now - until Mono's bug fix reaches Stable in Xamarin.