-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Crash in GlobalizationNative_GetSortHandle related to System.Globalization.GlobalizationMode #49073
Comments
Tagging subscribers to this area: @tarekgh, @safern Issue DetailsContext: dotnet/android#5669 In above PR we are experiencing crash in It might be related to these recent changes: #47999, dotnet/android@9ac280c The workaround: dotnet/android@438afbb
|
Tagging subscribers to 'linkable-framework': @eerhardt, @vitek-karas, @LakshanF, @tannergooding, @sbomer Issue DetailsContext: dotnet/android#5669 In above PR we are experiencing crash in It might be related to these recent changes: #47999, dotnet/android@9ac280c The workaround: dotnet/android@438afbb
|
Just guessing by looking at the code, but is anything explicitly loading ICU on the xamarin android app? What could be happening is the following line getting trimmed away: runtime/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs Line 25 in a43deba
And nothing else is loading ICU. So the
|
@eerhardt is it possible to trim the line you pointed at even Invariant mode is off? |
My assumption is that |
I can repro this outside of Xamarin, just on a plain linux machine using the latest SDK. <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<InvariantGlobalization>false</InvariantGlobalization>
</PropertyGroup>
</Project> using System;
using System.Configuration;
namespace ConsoleApp7
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(DateTime.Now);
}
}
}
|
Bump net6 preview2 version, be in sync with iOS https://github.com/xamarin/xamarin-macios/blob/871e7b1cd0ca0e8434e94c8eedb168f33d5da2e8/Make.config#L500 Bump runtime pack version. Added `System.Private.CoreLib.xml` as workaround for the crash in `GlobalizationNative_GetSortHandle`. Context: dotnet/runtime#49073 Stop using `mono_register_config_for_assembly` and `mono_config_parse_memory` functions, they are gone from net6 runtime. `BuildReleaseArm64` test, net6 apk size difference before/after Simple XA: ``` Summary: + 0 Other entries 0.00% (of 58,410) + 0 Dalvik executables 0.00% (of 316,728) - 7,699 Assemblies -1.10% (of 696,896) - 41,704 Shared libraries -0.80% (of 5,196,608) - 14,848 Uncompressed assemblies -1.09% (of 1,361,408) - 36,864 Package size difference -1.25% (of 2,946,926) ``` XF/XA: ``` Summary: + 0 Other entries 0.00% (of 917,033) + 0 Dalvik executables 0.00% (of 3,455,720) - 15,978 Assemblies -0.28% (of 5,682,747) - 41,704 Shared libraries -0.79% (of 5,271,800) - 31,232 Uncompressed assemblies -0.25% (of 12,712,960) - 45,056 Package size difference -0.45% (of 9,952,454) ``` Co-authored-by: Jonathan Peppers <jonathan.peppers@microsoft.com> Co-authored-by: Marek Habersack <grendel@twistedcode.net>
@eerhardt assumption looks to be correct. Modifying the program to be: using System;
using System.Runtime.InteropServices;
namespace ConsoleApp7
{
class Program
{
static void Main(string[] args)
{
LoadICU();
Console.WriteLine(DateTime.Now);
}
[DllImport("libSystem.Globalization.Native", EntryPoint = "GlobalizationNative_LoadICU")]
internal static extern int LoadICU();
}
} Allows everything to work as expected:
|
Actually, I believe this is a linker bug in that it is trimming the static initialization logic for |
The illink XML doesn't look right: <type fullname="System.Globalization.GlobalizationMode">
< ... >
<method signature="System.Boolean get_Invariant()" body="stub" value="false" feature="System.Globalization.Invariant" featurevalue="false" />
</type> I don't think we can make a promise that Invariant will always be false. On Browser, we will fall back to Invariant if ICU cannot be loaded. Taking the ICU codepaths in that case won't lead to happiness: runtime/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs Lines 25 to 37 in 79ae74f
This is not a publish-time constant, at least not in browser. |
This doesn't look like a good user experience to me and we should fix that. The message needs to be updated anyway as it does not make sense of other platforms too (e.g tvOS). /cc @lewing |
I don't follow. This is a feature switch that the developer (or SDK) set and explicitly said |
Right, but the code as it is there can fallback to this returning true if ICU cannot load on Browser targets (see the quoted code above). If we want to keep that behavior, Invariant==false cannot be a publish-time constant because the generated code needs to be able to handle the "we wanted to use ICU but ICU didn't load so we use Invariant" case. ICU doesn't load at all right now, so that needs to be fixed first of course. |
This would be trivial to handle and would integrate with existing code if we had a way to substitute app config switches rather than method bodies. Most of the BCL, and other apps in several cases, rely on logic similar or in addition to the following initializing a static readonly field (including if (!AppContext.TryGetSwitch(switchName, out bool ret))
{
string? switchValue = Environment.GetEnvironmentVariable(envVariable);
if (switchValue != null)
{
ret = bool.IsTrueStringIgnoreCase(switchValue) || switchValue.Equals("1");
}
}
return ret; This works and works nicely because the JIT optimizes Many of the method bodies we are substituting have corresponding app config switches and potentially logic (like the above) that executes in addition to the replaced method body. |
Yes, it would be the ideal user experience and avoid a lot of issues. It was discussed e.g. here: dotnet/linker#1112 (comment). It's problematic because illink needs to basically interpret the entire class constructors involved (the key thing is that it needs to understand the entire cctor - not just the small part that assigns the value - because the assignment could be behind complex control flow). |
Could this be handled in two parts? That is, allow the user to specify that an app config switch is |
What is the pattern the JIT can optimize? Are you saying this https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/LocalAppContextSwitches.cs is not something JIT can see through? |
The JIT optimizes
Right, that will not be optimized AFAIK. The JIT has to assume non readonly If it was instead the following (like public static bool SerializationGuard { get; } = GetSwitchValue("Switch.System.Runtime.Serialization.SerializationGuard"); |
I don't think there are many if any at all. I believe you either end up with something like the linker is doing today which is a "new concept" and allows the root block to be optimized as "appropriate" but which may require the .NET code to be refactored to account for it or you teach the linker to work with the existing .NET optimization concepts. For this particular case, it could be transitioned to an explicit static constructor that ensures |
If memory serves, the browser fallback here was primarily added to keep things limping along while we were integrating the icu data loading changes through the stack. If the fallback is causing issues we can probably turn it back into an error now (and update the error message). |
@safern, @tarekgh; do you see any problems with refactoring this to have an explicit static constructor that calls As best as I can tell, I think it would be refactored so that |
I've opened #49391 to track this. |
@tannergooding I don't see a problem if we guarantee the order of execution of other static globalization properties. We need to ensure LoadICU is called as early as possible. One side point, there is more properties will be added to the GlobalizationMode #47301. |
If you move LoadIcu to explicit static constructor on Unix, |
Just throwing this out a possible solution: We could change my initial change (#47999) to be browser-wasm specific. It saves roughly 4 KB (.br compressed) in the default blazorwasm template, which is worthwhile for that environment. But for other targets (even Xamarin), it doesn't seem like a significant gain. |
Is it possible for Likewise, I think it would be good to determine how to handle these issues long term. This is one case where the method body replacement is problematic and more may come up as the targeted trimmings get more complex. |
I think you can make the explicit static constructor work well if you split GlobalizationMode into two types: One type that has just the invariant mode flag and that will be responsible for initializing the ICU as appropriate, and the second type has the rest of the globalization properties. |
Right, you need to be creative to fit the pattern that method body replacement is able to handle. We have to do refactoring pretty much every time we try to make something trimmable. |
Another alternative, if we want to keep the fallback behavior where |
The issue there is all the code in CoreLib is switching off of runtime/src/libraries/System.Private.CoreLib/src/System/Globalization/CompareInfo.cs Lines 1471 to 1478 in e341abf
|
Right - I'm only mentioning it as another option in case we want to keep the browser fallback that tries to set |
That would completely undo any size gains made by #47999 |
Another scenario to consider here is App Local ICU. App Local ICU does more than just calling That currently happens here: runtime/src/libraries/System.Private.CoreLib/src/System/Globalization/GlobalizationMode.Unix.cs Lines 19 to 22 in 220b01a
|
The Xamarin.Android team re-encountered this issue, or a variation on it, in: dotnet/android#5954 Our "workaround" was to effectively revert @eerhardt 's change in dotnet/android@9ac280c, and instead leave <InvariantGlobalization Condition="'$(InvariantGlobalization)' != 'true'"></InvariantGlobalization> The crash we saw @eerhardt 's previous comment:
Is there anything we need to do to properly fix and resolve this issue? |
For what it's worth, in a P5 runtime branch, a trimmed
Whereas the trimmed version in XA looks like:
Are we (or am I) pointing at different versions of the linker? |
Move the LoadICU logic into a static ctor, so it runs early in the process, but not as part of querying the GlobalizationMode.Invariant property. This allows for LoadICU to still be invoked, even when the app is trimmed and GlobalizationMode.Invariant is hard-coded by the linker. While I was changing this code, I also removed the workaround in Browser builds for swallowing errors being returned from LoadICU. Fix dotnet#49073 Fix dotnet#49391
FYI - I am taking a look at this. I have the beginnings of a fix here: https://github.com/eerhardt/runtime/tree/FixInvariantModeTrimming. Writing tests now. |
* Fix InvariantGlobalization=false in a trimmed app. Move the LoadICU logic into a static ctor, so it runs early in the process, but not as part of querying the GlobalizationMode.Invariant property. This allows for LoadICU to still be invoked, even when the app is trimmed and GlobalizationMode.Invariant is hard-coded by the linker. While I was changing this code, I also removed the workaround in Browser builds for swallowing errors being returned from LoadICU. Fix #49073 Fix #49391 * Add trimming tests for InvariantGlobalization * Update the LoadICU message for mobile workloads. * Respond to PR feedback. - Split the substitutions of GlobalizationMode.Invariant between true and false - Add a trimming test that ensures Invariant=true trims everything it is supposed to * Add checks for all mobile platforms
The linked runtime issue is fixed: dotnet/runtime#49073 We should be able to remove this now.
Context: dotnet/android#5669
In above PR we are experiencing crash in
GlobalizationNative_GetSortHandle
function. I found out that it happens only in trimmed apps. After further investigation, it can be worked around by preservingSystem.Globalization.GlobalizationMode
type.It might be related to these recent changes: #47999, dotnet/android@9ac280c
The workaround: dotnet/android@438afbb
The text was updated successfully, but these errors were encountered: