Skip to content

Commit a9706b6

Browse files
authored
[Xamarin.Android.Build.Tasks] LLVM Marshal Methods by Default, Take 2 (#8925)
Context: 6836818 Context: 8bc7a3e Commit 8bc7a3e enabled LLVM Marshal Methods by default, and commit 6836818 *disabled* LLVM Marshal Methods by default, because it appeared to contribute to hangs in MAUI+Blazor apps. After lots of additional cleanup, refactoring, and investigation… we *still* don't understand why MAUI+Blazor apps hang, and we want the app time startup improvements that LLVM Marshal Methods allow. Square this circle by enabling LLVM Marshal Methods by default, *unless* Blazor is detected, in which case LLVM Marshal Methods will be *disabled* automatically. ~~ App startup time improvements ~~ Measurements are across 50 runs if a test app, with the fastest and slowest runs removed from the set. * Displayed time * Best result: **15.73%** faster (default settings *without* ProfiledAOT and compression) * Default settings result: **12.66%** faster. * Native to managed transition * Best result: **1.31%** faster * Default settings result: **0.35%** slower * Total managed runtime init * Best result: **2.55%** faster (default settings without ProfiledAOT) * Default settings result: **0.71%** faster ~~ Build Target Interdependencies ~~ While developing this change, we ran into an odd bug, via the `InstallAndRunTests.EnableAndroidStripILAfterAOT(false)` test: % dotnet new android % dotnet build -c Release -v:diag -p:AndroidEnableMarshalMethods=true \ -p:AndroidStripILAfterAOT=true -p:AndroidEnableProfiledAot=false > b.txt % dotnet build -c Release -v:diag -p:AndroidEnableMarshalMethods=true \ -p:AndroidStripILAfterAOT=true -p:AndroidEnableProfiledAot=false -t:Install > i.txt % dotnet build -c Release -t:StartAndroidActivity The app immediately crashes on startup; from `adb logcat`: F mono-rt : [ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidProgramException: Invalid IL code in Android.Runtime.JNIEnvInit:Initialize (Android.Runtime.JNIEnvInit/JnienvInitializeArgs*): method body is empty. The reason fo the crash is that during the first `dotnet build` invocation, the marshal methods classifier works as it should and, consequently, the marshal methods rewriter *modifies the assemblies*: removes connector methods, generates wrapper methods, etc etc. As part of this, the `_GenerateJavaStubs` target eventually creates a stamp file which it then uses to decide whether all the inputs are up to date with regards to their corresponding outputs. However, at the end of the first `dotnet build`, `ILStrip` runs which modifies assemblies *after* marshal methods processing is done. When we run the `dotnet build -t:Install` command, the the `_GenerateJavaStubs` target runs *again* as MSBuild notices that the stamp file is older than some of the inputs: assemblies modified by `ILStrip` after `_GenerateJavaStubs` created its stamp file. This causes the target to run completely: Target "_GenerateJavaStubs: (TargetId:337)" in file "/usr/local/share/dotnet/packs/Microsoft.Android.Sdk.Darwin/34.0.95/tools/Xamarin.Android.Common.targets" from project "…/gxa-8925.csproj" (target "_GeneratePackageManagerJava" depends on it): Building target "_GenerateJavaStubs" completely. Input file "obj/Release/net8.0-android/android-arm/linked/gxa-8925.dll" is newer than output file "obj/Release/net8.0-android/stamp/_GenerateJavaStubs.stamp". which causes the whole marshal methods processing to be initiated again, but this time the connector methods and anything else the classifier looks for aren't there, because they were previously removed by the rewriter during the first `dotnet build` command. This, in turn, causes the classifier to decide that types need to be registered dynamically, but they can't because the connector methods which are required to create delegates are no longer there and we get a runtime crash instead. The solution is to update the `_GenerateJavaStubs` stamp file after `ILStrip` is done, within the `_AndroidAotCompilation` target, if it modified any assemblies. One problem with that is that the path to the stamp file's directory is different when `_GenerateJavaStubs` runs during the build phase, and when AOT and `ILStrip` run. In the former case, the stamp file is created in `obj/${Configuration}/stamp` directory, in the latter case in `obj/${Configuration}/android-ARCH/stamp` directory, but *both* locations are initialized in the same spot: at the top of `Xamarin.Android.Common.targets`. In effect, after `ILStrip` runs, we don't really know where `_GenerateJavaStubs` created its stamp file. Workaround this by taking advantage of the fact that we know where the stamp directories are in relation to each other. It's a hack, but if the relation is somehow broken, the `InstallAndRunTests.EnableAndroidStripILAfterAOT(false)` test will break again.
1 parent 935808b commit a9706b6

File tree

4 files changed

+95
-73
lines changed

4 files changed

+95
-73
lines changed

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Aot.targets

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,30 @@ They run in a context of an inner build with a single $(RuntimeIdentifier).
143143
SourceFiles="@(_ILStripUpdatedAssemblies)"
144144
DestinationFiles="@(_ILStripUpdatedAssemblies->'%(UntrimmedAssemblyFilePath)')"
145145
/>
146+
<ItemGroup>
147+
<_UpdateStamp Include="@(_ILStripUpdatedAssemblies)" Condition=" '$(AndroidStripILAfterAOT)' == 'true' and '%(_ILStripUpdatedAssemblies.ILStripped)' == 'true' " />
148+
</ItemGroup>
149+
150+
<!-- We must update the stamp file used by `_GenerateJavaStubs`, but `$(_AndroidStampDirectory)` **here** is inside a per-RID parent
151+
directory (e.g. `obj/Release/android-arm64/stamp`) and not the "top level" one (`obj/Release/stamp`). Since **both** are set
152+
in the same place (in `Xamarin.Android.Common.targets`), we don't know where the "top level" one really is. Hence the hack.
153+
If the locations or their relative paths change, then the `EnableAndroidStripILAfterAOTFalse` test will fail.
154+
155+
However, there should be a better way to do it... Ideally, when marshal methods are enabled **nothing** should modify any
156+
assemblies after marshal method classifier and rewriter runs.
157+
-->
158+
<PropertyGroup>
159+
<_StampDir>$(_AndroidStampDirectory)\..\..\stamp</_StampDir>
160+
</PropertyGroup>
161+
<MakeDir
162+
Condition=" '$(AndroidStripILAfterAOT)' == 'true' and '@(_UpdateStamp->Count())' &gt; 0 "
163+
Directories="$(_StampDir)"
164+
/>
165+
<Touch
166+
Condition=" '$(AndroidStripILAfterAOT)' == 'true' and '@(_UpdateStamp->Count())' &gt; 0 "
167+
Files="$(_StampDir)\_GenerateJavaStubs.stamp"
168+
AlwaysCreate="True"
169+
/>
146170
<WriteLinesToFile
147171
File="$(_AndroidStampDirectory)_AndroidAot.stamp"
148172
Lines="@(_MonoAOTCompiledAssemblies->'%(LibraryFile)')"

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,28 @@
55
"Size": 3036
66
},
77
"classes.dex": {
8-
"Size": 377764
8+
"Size": 19476
99
},
1010
"lib/arm64-v8a/lib__Microsoft.Android.Resource.Designer.dll.so": {
1111
"Size": 1027
1212
},
1313
"lib/arm64-v8a/lib_Java.Interop.dll.so": {
14-
"Size": 64231
14+
"Size": 64655
1515
},
1616
"lib/arm64-v8a/lib_Mono.Android.dll.so": {
17-
"Size": 91590
17+
"Size": 92217
1818
},
1919
"lib/arm64-v8a/lib_Mono.Android.Runtime.dll.so": {
20-
"Size": 5315
20+
"Size": 5320
2121
},
2222
"lib/arm64-v8a/lib_System.Console.dll.so": {
23-
"Size": 6539
23+
"Size": 6541
2424
},
2525
"lib/arm64-v8a/lib_System.Linq.dll.so": {
26-
"Size": 8525
26+
"Size": 8524
2727
},
2828
"lib/arm64-v8a/lib_System.Private.CoreLib.dll.so": {
29-
"Size": 570166
29+
"Size": 571267
3030
},
3131
"lib/arm64-v8a/lib_System.Runtime.dll.so": {
3232
"Size": 2544
@@ -35,7 +35,7 @@
3535
"Size": 4020
3636
},
3737
"lib/arm64-v8a/lib_UnnamedProject.dll.so": {
38-
"Size": 2933
38+
"Size": 2931
3939
},
4040
"lib/arm64-v8a/libarc.bin.so": {
4141
"Size": 1586
@@ -44,10 +44,10 @@
4444
"Size": 87352
4545
},
4646
"lib/arm64-v8a/libmonodroid.so": {
47-
"Size": 476432
47+
"Size": 492104
4848
},
4949
"lib/arm64-v8a/libmonosgen-2.0.so": {
50-
"Size": 3138768
50+
"Size": 3154304
5151
},
5252
"lib/arm64-v8a/libSystem.Globalization.Native.so": {
5353
"Size": 67248
@@ -56,16 +56,16 @@
5656
"Size": 723560
5757
},
5858
"lib/arm64-v8a/libSystem.Native.so": {
59-
"Size": 94720
59+
"Size": 95296
6060
},
6161
"lib/arm64-v8a/libSystem.Security.Cryptography.Native.Android.so": {
6262
"Size": 155568
6363
},
6464
"lib/arm64-v8a/libxamarin-app.so": {
65-
"Size": 12696
65+
"Size": 17952
6666
},
6767
"META-INF/BNDLTOOL.RSA": {
68-
"Size": 1221
68+
"Size": 1223
6969
},
7070
"META-INF/BNDLTOOL.SF": {
7171
"Size": 3266
@@ -98,5 +98,5 @@
9898
"Size": 1904
9999
}
100100
},
101-
"PackageSize": 2742805
101+
"PackageSize": 2681365
102102
}

0 commit comments

Comments
 (0)