diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets
index e145c311485..a6f5b0f65ec 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets
@@ -132,7 +132,8 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
$(_NdkAbi)-linux-android$(_NDKApiLevel)-clang$(_NdkWrapperScriptExt)
llvm-objcopy
-
+
false
@@ -197,13 +198,9 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<_NdkLibs Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower())'))" />
-
- <_NdkLibs Include="$(_NdkSysrootDir)libc++_static.a" />
- <_NdkLibs Include="$(_NdkSysrootDir)libc++abi.a" />
-
-
+
@@ -362,12 +359,10 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android.
<_NativeAotSystemLibraries Include="c" />
-
<_NativeAotLinkLibraries Include="@(RuntimePackAsset->WithMetadataValue('Filename', 'libnaot-android.$(Configuration.ToLower())-static-$(Configuration.ToLower())'))" />
- <_NativeAotLinkLibraries Include="$(_NativeAotRuntimePackNativeDir)libc++_static.a" />
- <_NativeAotLinkLibraries Include="$(_NativeAotRuntimePackNativeDir)libc++abi.a" />
<_NativeAotLinkLibraries Include="@(_NdkLibs)" />
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc
index 15e3aae8ee9..135a71476c5 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.CoreCLR.apkdesc
@@ -5,19 +5,19 @@
"Size": 3036
},
"classes.dex": {
- "Size": 400044
+ "Size": 405624
},
"lib/arm64-v8a/libassembly-store.so": {
- "Size": 3098192
+ "Size": 3124888
},
"lib/arm64-v8a/libclrjit.so": {
- "Size": 3223752
+ "Size": 3222704
},
"lib/arm64-v8a/libcoreclr.so": {
- "Size": 5771784
+ "Size": 5757272
},
"lib/arm64-v8a/libmonodroid.so": {
- "Size": 1366648
+ "Size": 1279264
},
"lib/arm64-v8a/libSystem.Globalization.Native.so": {
"Size": 72344
@@ -26,22 +26,13 @@
"Size": 1280336
},
"lib/arm64-v8a/libSystem.Native.so": {
- "Size": 100552
+ "Size": 101872
},
"lib/arm64-v8a/libSystem.Security.Cryptography.Native.Android.so": {
"Size": 162000
},
"lib/arm64-v8a/libxamarin-app.so": {
- "Size": 20424
- },
- "META-INF/BNDLTOOL.RSA": {
- "Size": 1223
- },
- "META-INF/BNDLTOOL.SF": {
- "Size": 2091
- },
- "META-INF/MANIFEST.MF": {
- "Size": 1964
+ "Size": 20776
},
"res/drawable-hdpi-v4/icon.png": {
"Size": 2178
@@ -68,5 +59,5 @@
"Size": 1904
}
},
- "PackageSize": 7632514
-}
+ "PackageSize": 7620027
+}
\ No newline at end of file
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc
index 42c7d46617e..20aeaa019a6 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.MonoVM.apkdesc
@@ -8,31 +8,31 @@
"Size": 22384
},
"lib/arm64-v8a/lib__Microsoft.Android.Resource.Designer.dll.so": {
- "Size": 18232
+ "Size": 18224
},
"lib/arm64-v8a/lib_Java.Interop.dll.so": {
- "Size": 87720
+ "Size": 88144
},
"lib/arm64-v8a/lib_Mono.Android.dll.so": {
- "Size": 118392
+ "Size": 118312
},
"lib/arm64-v8a/lib_Mono.Android.Runtime.dll.so": {
- "Size": 26848
+ "Size": 26864
},
"lib/arm64-v8a/lib_System.Console.dll.so": {
"Size": 24360
},
"lib/arm64-v8a/lib_System.Linq.dll.so": {
- "Size": 25432
+ "Size": 25448
},
"lib/arm64-v8a/lib_System.Private.CoreLib.dll.so": {
- "Size": 638568
+ "Size": 638000
},
"lib/arm64-v8a/lib_System.Runtime.dll.so": {
"Size": 20224
},
"lib/arm64-v8a/lib_System.Runtime.InteropServices.dll.so": {
- "Size": 19752
+ "Size": 19760
},
"lib/arm64-v8a/lib_UnnamedProject.dll.so": {
"Size": 19968
@@ -44,10 +44,10 @@
"Size": 36416
},
"lib/arm64-v8a/libmonodroid.so": {
- "Size": 1385800
+ "Size": 1294440
},
"lib/arm64-v8a/libmonosgen-2.0.so": {
- "Size": 3111840
+ "Size": 3111632
},
"lib/arm64-v8a/libSystem.Globalization.Native.so": {
"Size": 72344
@@ -62,7 +62,7 @@
"Size": 162000
},
"lib/arm64-v8a/libxamarin-app.so": {
- "Size": 19832
+ "Size": 19680
},
"res/drawable-hdpi-v4/icon.png": {
"Size": 2178
@@ -89,5 +89,5 @@
"Size": 1904
}
},
- "PackageSize": 3254606
+ "PackageSize": 3221838
}
\ No newline at end of file
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc
index 75e2332c446..305a7566aa0 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.NativeAOT.apkdesc
@@ -5,19 +5,10 @@
"Size": 3124
},
"classes.dex": {
- "Size": 25400
+ "Size": 25304
},
"lib/arm64-v8a/libUnnamedProject.so": {
- "Size": 5072296
- },
- "META-INF/BNDLTOOL.RSA": {
- "Size": 1221
- },
- "META-INF/BNDLTOOL.SF": {
- "Size": 1211
- },
- "META-INF/MANIFEST.MF": {
- "Size": 1084
+ "Size": 4446712
},
"res/drawable-hdpi-v4/icon.png": {
"Size": 2178
@@ -44,5 +35,5 @@
"Size": 1904
}
},
- "PackageSize": 2126818
+ "PackageSize": 1897243
}
\ No newline at end of file
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.apkdesc
index 3e9a925186f..a43a6641838 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.apkdesc
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.CoreCLR.apkdesc
@@ -5,10 +5,10 @@
"Size": 6652
},
"classes.dex": {
- "Size": 9444112
+ "Size": 9411640
},
"classes2.dex": {
- "Size": 156448
+ "Size": 157320
},
"kotlin/annotation/annotation.kotlin_builtins": {
"Size": 928
@@ -32,16 +32,16 @@
"Size": 2396
},
"lib/arm64-v8a/libassembly-store.so": {
- "Size": 14076232
+ "Size": 14100448
},
"lib/arm64-v8a/libclrjit.so": {
- "Size": 3223752
+ "Size": 3222704
},
"lib/arm64-v8a/libcoreclr.so": {
- "Size": 5771784
+ "Size": 5757272
},
"lib/arm64-v8a/libmonodroid.so": {
- "Size": 1366648
+ "Size": 1279264
},
"lib/arm64-v8a/libSystem.Globalization.Native.so": {
"Size": 72344
@@ -50,13 +50,13 @@
"Size": 1280336
},
"lib/arm64-v8a/libSystem.Native.so": {
- "Size": 100552
+ "Size": 101872
},
"lib/arm64-v8a/libSystem.Security.Cryptography.Native.Android.so": {
"Size": 162000
},
"lib/arm64-v8a/libxamarin-app.so": {
- "Size": 147264
+ "Size": 147608
},
"META-INF/androidx.activity_activity.version": {
"Size": 6
@@ -208,12 +208,6 @@
"META-INF/androidx.viewpager2_viewpager2.version": {
"Size": 6
},
- "META-INF/BNDLTOOL.RSA": {
- "Size": 1221
- },
- "META-INF/BNDLTOOL.SF": {
- "Size": 90142
- },
"META-INF/com.android.tools/proguard/coroutines.pro": {
"Size": 1345
},
@@ -238,9 +232,6 @@
"META-INF/kotlinx_coroutines_core.version": {
"Size": 5
},
- "META-INF/MANIFEST.MF": {
- "Size": 90015
- },
"META-INF/maven/com.google.guava/listenablefuture/pom.properties": {
"Size": 96
},
@@ -478,9 +469,6 @@
"res/color-night-v8/material_timepicker_modebutton_tint.xml": {
"Size": 340
},
- "res/color-v21/abc_btn_colored_borderless_text_material.xml": {
- "Size": 464
- },
"res/color-v23/abc_btn_colored_borderless_text_material.xml": {
"Size": 500
},
@@ -514,9 +502,6 @@
"res/color/abc_background_cache_hint_selector_material_light.xml": {
"Size": 468
},
- "res/color/abc_btn_colored_text_material.xml": {
- "Size": 604
- },
"res/color/abc_hint_foreground_material_dark.xml": {
"Size": 564
},
@@ -544,24 +529,6 @@
"res/color/abc_secondary_text_material_light.xml": {
"Size": 464
},
- "res/color/abc_tint_btn_checkable.xml": {
- "Size": 728
- },
- "res/color/abc_tint_default.xml": {
- "Size": 1224
- },
- "res/color/abc_tint_edittext.xml": {
- "Size": 772
- },
- "res/color/abc_tint_seek_thumb.xml": {
- "Size": 604
- },
- "res/color/abc_tint_spinner.xml": {
- "Size": 772
- },
- "res/color/abc_tint_switch_track.xml": {
- "Size": 768
- },
"res/color/checkbox_themeable_attribute_color.xml": {
"Size": 464
},
@@ -1699,9 +1666,6 @@
"res/drawable/mtrl_ic_error.xml": {
"Size": 644
},
- "res/drawable/mtrl_popupmenu_background_dark.xml": {
- "Size": 740
- },
"res/drawable/mtrl_popupmenu_background.xml": {
"Size": 740
},
@@ -1798,15 +1762,6 @@
"res/layout-v21/notification_template_icon_group.xml": {
"Size": 988
},
- "res/layout-v22/abc_alert_dialog_button_bar_material.xml": {
- "Size": 1584
- },
- "res/layout-v22/material_timepicker_dialog.xml": {
- "Size": 3184
- },
- "res/layout-v22/mtrl_alert_dialog_actions.xml": {
- "Size": 1764
- },
"res/layout-v26/abc_screen_toolbar.xml": {
"Size": 1560
},
@@ -1844,7 +1799,7 @@
"Size": 1684
},
"res/layout/abc_alert_dialog_button_bar_material.xml": {
- "Size": 1536
+ "Size": 1584
},
"res/layout/abc_alert_dialog_material.xml": {
"Size": 2648
@@ -2009,7 +1964,7 @@
"Size": 1208
},
"res/layout/material_timepicker_dialog.xml": {
- "Size": 3132
+ "Size": 3184
},
"res/layout/material_timepicker_textinput_display.xml": {
"Size": 684
@@ -2018,7 +1973,7 @@
"Size": 1136
},
"res/layout/mtrl_alert_dialog_actions.xml": {
- "Size": 1620
+ "Size": 1764
},
"res/layout/mtrl_alert_dialog_title.xml": {
"Size": 956
@@ -2276,8 +2231,8 @@
"Size": 268
},
"resources.arsc": {
- "Size": 812848
+ "Size": 794696
}
},
- "PackageSize": 21263139
-}
+ "PackageSize": 21151309
+}
\ No newline at end of file
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc
index 043500537a1..5a802f51b13 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc
@@ -35,91 +35,91 @@
"Size": 25360
},
"lib/arm64-v8a/lib_Java.Interop.dll.so": {
- "Size": 96344
+ "Size": 96784
},
"lib/arm64-v8a/lib_Mono.Android.dll.so": {
- "Size": 542848
+ "Size": 542448
},
"lib/arm64-v8a/lib_Mono.Android.Runtime.dll.so": {
- "Size": 26848
+ "Size": 26864
},
"lib/arm64-v8a/lib_mscorlib.dll.so": {
"Size": 21408
},
"lib/arm64-v8a/lib_netstandard.dll.so": {
- "Size": 23048
+ "Size": 23056
},
"lib/arm64-v8a/lib_System.Collections.dll.so": {
- "Size": 33944
+ "Size": 33952
},
"lib/arm64-v8a/lib_System.Collections.NonGeneric.dll.so": {
- "Size": 25608
+ "Size": 25624
},
"lib/arm64-v8a/lib_System.Collections.Specialized.dll.so": {
- "Size": 23800
+ "Size": 23816
},
"lib/arm64-v8a/lib_System.ComponentModel.dll.so": {
- "Size": 19560
+ "Size": 19568
},
"lib/arm64-v8a/lib_System.ComponentModel.Primitives.dll.so": {
"Size": 21296
},
"lib/arm64-v8a/lib_System.ComponentModel.TypeConverter.dll.so": {
- "Size": 43600
+ "Size": 43608
},
"lib/arm64-v8a/lib_System.Console.dll.so": {
- "Size": 24392
+ "Size": 24400
},
"lib/arm64-v8a/lib_System.Core.dll.so": {
- "Size": 19424
+ "Size": 19432
},
"lib/arm64-v8a/lib_System.Diagnostics.TraceSource.dll.so": {
- "Size": 24584
+ "Size": 24592
},
"lib/arm64-v8a/lib_System.dll.so": {
- "Size": 19784
+ "Size": 19792
},
"lib/arm64-v8a/lib_System.Drawing.dll.so": {
"Size": 19400
},
"lib/arm64-v8a/lib_System.Drawing.Primitives.dll.so": {
- "Size": 30016
+ "Size": 30032
},
"lib/arm64-v8a/lib_System.Formats.Asn1.dll.so": {
- "Size": 50968
+ "Size": 50976
},
"lib/arm64-v8a/lib_System.IO.Compression.Brotli.dll.so": {
- "Size": 29536
+ "Size": 29552
},
"lib/arm64-v8a/lib_System.IO.Compression.dll.so": {
- "Size": 34600
+ "Size": 34616
},
"lib/arm64-v8a/lib_System.IO.IsolatedStorage.dll.so": {
- "Size": 28232
+ "Size": 28240
},
"lib/arm64-v8a/lib_System.Linq.dll.so": {
- "Size": 47912
+ "Size": 47920
},
"lib/arm64-v8a/lib_System.Linq.Expressions.dll.so": {
- "Size": 185968
+ "Size": 185976
},
"lib/arm64-v8a/lib_System.Net.Http.dll.so": {
- "Size": 86680
+ "Size": 86688
},
"lib/arm64-v8a/lib_System.Net.Primitives.dll.so": {
- "Size": 42280
+ "Size": 42288
},
"lib/arm64-v8a/lib_System.Net.Requests.dll.so": {
- "Size": 21520
+ "Size": 21528
},
"lib/arm64-v8a/lib_System.ObjectModel.dll.so": {
- "Size": 26968
+ "Size": 26984
},
"lib/arm64-v8a/lib_System.Private.CoreLib.dll.so": {
- "Size": 999864
+ "Size": 1000432
},
"lib/arm64-v8a/lib_System.Private.DataContractSerialization.dll.so": {
- "Size": 217808
+ "Size": 217816
},
"lib/arm64-v8a/lib_System.Private.Uri.dll.so": {
"Size": 62216
@@ -128,25 +128,25 @@
"Size": 236968
},
"lib/arm64-v8a/lib_System.Private.Xml.Linq.dll.so": {
- "Size": 35464
+ "Size": 35480
},
"lib/arm64-v8a/lib_System.Runtime.dll.so": {
- "Size": 20352
+ "Size": 20360
},
"lib/arm64-v8a/lib_System.Runtime.InteropServices.dll.so": {
- "Size": 19752
+ "Size": 19760
},
"lib/arm64-v8a/lib_System.Runtime.Numerics.dll.so": {
"Size": 63312
},
"lib/arm64-v8a/lib_System.Runtime.Serialization.dll.so": {
- "Size": 19328
+ "Size": 19336
},
"lib/arm64-v8a/lib_System.Runtime.Serialization.Formatters.dll.so": {
- "Size": 20296
+ "Size": 20304
},
"lib/arm64-v8a/lib_System.Runtime.Serialization.Primitives.dll.so": {
- "Size": 21424
+ "Size": 21432
},
"lib/arm64-v8a/lib_System.Security.Cryptography.dll.so": {
"Size": 82024
@@ -155,10 +155,10 @@
"Size": 194176
},
"lib/arm64-v8a/lib_System.Xml.dll.so": {
- "Size": 19216
+ "Size": 19224
},
"lib/arm64-v8a/lib_System.Xml.Linq.dll.so": {
- "Size": 19240
+ "Size": 19248
},
"lib/arm64-v8a/lib_UnnamedProject.dll.so": {
"Size": 22040
@@ -239,10 +239,10 @@
"Size": 36416
},
"lib/arm64-v8a/libmonodroid.so": {
- "Size": 1385800
+ "Size": 1294440
},
"lib/arm64-v8a/libmonosgen-2.0.so": {
- "Size": 3111840
+ "Size": 3111632
},
"lib/arm64-v8a/libSystem.Globalization.Native.so": {
"Size": 72344
@@ -257,7 +257,7 @@
"Size": 162000
},
"lib/arm64-v8a/libxamarin-app.so": {
- "Size": 350616
+ "Size": 350464
},
"META-INF/androidx.activity_activity.version": {
"Size": 6
@@ -2435,5 +2435,5 @@
"Size": 794696
}
},
- "PackageSize": 11024231
+ "PackageSize": 10991463
}
\ No newline at end of file
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/NativeRuntimeComponents.cs b/src/Xamarin.Android.Build.Tasks/Utilities/NativeRuntimeComponents.cs
index 4773d0cd1c1..cf5e40120ea 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/NativeRuntimeComponents.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/NativeRuntimeComponents.cs
@@ -129,10 +129,6 @@ public NativeRuntimeComponents (ITaskItem[]? monoComponents)
new AndroidArchive ("libxa-shared-bits-release.a"),
new AndroidArchive ("libxamarin-startup-release.a"),
- // C++ standard library
- new CplusPlusArchive ("libc++_static.a"),
- new CplusPlusArchive ("libc++abi.a"),
-
// LLVM clang built-ins archives
new ClangBuiltinsArchive ("aarch64"),
new ClangBuiltinsArchive ("arm"),
diff --git a/src/native/clr/host/assembly-store.cc b/src/native/clr/host/assembly-store.cc
index bc79b646596..4121f0d1be4 100644
--- a/src/native/clr/host/assembly-store.cc
+++ b/src/native/clr/host/assembly-store.cc
@@ -1,3 +1,4 @@
+#include
#include
#if defined (HAVE_LZ4)
@@ -29,7 +30,7 @@ auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData co
#if defined (HAVE_LZ4) && defined (RELEASE)
auto header = reinterpret_cast(e.image_data);
if (header->magic == COMPRESSED_DATA_MAGIC) {
- log_debug (LOG_ASSEMBLY, "Decompressing assembly '{}' from the assembly store"sv, name);
+ log_debug (LOG_ASSEMBLY, "Decompressing assembly '%.*s' from the assembly store", static_cast(name.length ()), name.data ());
if (FastTiming::enabled ()) [[unlikely]] {
internal_timing.start_event (TimingEventKind::AssemblyDecompression);
@@ -108,7 +109,7 @@ auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData co
)
);
} else {
- log_debug (LOG_ASSEMBLY, "Compressed assembly '{}' is smaller than when the application was built. Adjusting accordingly."sv, name);
+ log_debug (LOG_ASSEMBLY, "Compressed assembly '%.*s' is smaller than when the application was built. Adjusting accordingly.", static_cast(name.length ()), name.data ());
}
cad.uncompressed_file_size = header->uncompressed_length;
}
@@ -149,7 +150,7 @@ auto AssemblyStore::get_assembly_data (AssemblyStoreSingleAssemblyRuntimeData co
} else
#endif // def HAVE_LZ4 && def RELEASE
{
- log_debug (LOG_ASSEMBLY, "Assembly '{}' is not compressed in the assembly store"sv, name);
+ log_debug (LOG_ASSEMBLY, "Assembly '%.*s' is not compressed in the assembly store", static_cast(name.length ()), name.data ());
// HACK! START
// Currently, MAUI crashes when we return a pointer to read-only data, so we must copy
@@ -199,7 +200,7 @@ auto AssemblyStore::open_assembly (std::string_view const& name, int64_t &size)
if constexpr (Constants::is_debug_build) {
// In fastdev mode we might not have any assembly store.
if (assembly_store_hashes == nullptr) {
- log_warn (LOG_ASSEMBLY, "Assembly store not registered. Unable to look up assembly '{}'"sv, name);
+ log_warn (LOG_ASSEMBLY, "Assembly store not registered. Unable to look up assembly '%.*s'", static_cast(name.length ()), name.data ());
return nullptr;
}
}
@@ -207,13 +208,13 @@ auto AssemblyStore::open_assembly (std::string_view const& name, int64_t &size)
const AssemblyStoreIndexEntry *hash_entry = find_assembly_store_entry (name_hash, assembly_store_hashes, assembly_store.index_entry_count);
if (hash_entry == nullptr) [[unlikely]] {
size = 0;
- log_warn (LOG_ASSEMBLY, "Assembly '{}' (hash 0x{:x}) not found"sv, name, name_hash);
+ log_warn (LOG_ASSEMBLY, "Assembly '%.*s' (hash 0x%zx) not found", static_cast(name.length ()), name.data (), static_cast(name_hash));
return nullptr;
}
if (hash_entry->ignore != 0) {
size = 0;
- log_debug (LOG_ASSEMBLY, "Assembly '{}' ignored"sv, name);
+ log_debug (LOG_ASSEMBLY, "Assembly '%.*s' ignored", static_cast(name.length ()), name.data ());
return nullptr;
}
@@ -242,7 +243,7 @@ auto AssemblyStore::open_assembly (std::string_view const& name, int64_t &size)
log_debug (
LOG_ASSEMBLY,
- "Mapped: image_data == {:p}; debug_info_data == {:p}; config_data == {:p}; descriptor == {:p}; data size == {}; debug data size == {}; config data size == {}; name == '{}'"sv,
+ "Mapped: image_data == %p; debug_info_data == %p; config_data == %p; descriptor == %p; data size == %u; debug data size == %u; config data size == %u; name == '%.*s'",
static_cast(assembly_runtime_info.image_data),
static_cast(assembly_runtime_info.debug_info_data),
static_cast(assembly_runtime_info.config_data),
@@ -250,7 +251,8 @@ auto AssemblyStore::open_assembly (std::string_view const& name, int64_t &size)
assembly_runtime_info.descriptor->data_size,
assembly_runtime_info.descriptor->debug_data_size,
assembly_runtime_info.descriptor->config_data_size,
- name
+ static_cast(name.length ()),
+ name.data ()
);
}
@@ -264,7 +266,7 @@ void AssemblyStore::map (int fd, std::string_view const& apk_path, std::string_v
detail::mmap_info assembly_store_map = Util::mmap_file (fd, offset, size, store_path);
auto [payload_start, payload_size] = Util::get_wrapper_dso_payload_pointer_and_size (assembly_store_map, store_path);
- log_debug (LOG_ASSEMBLY, "Adjusted assembly store pointer: {:p}; size: {}"sv, payload_start, payload_size);
+ log_debug (LOG_ASSEMBLY, "Adjusted assembly store pointer: %p; size: %zu", payload_start, payload_size);
auto header = static_cast(payload_start);
auto get_full_store_path = [&apk_path, &store_path]() -> std::string {
@@ -312,5 +314,5 @@ void AssemblyStore::map (int fd, std::string_view const& apk_path, std::string_v
assembly_store.assemblies = reinterpret_cast(assembly_store.data_start + header_size + header->index_size);
assembly_store_hashes = reinterpret_cast(assembly_store.data_start + header_size);
- log_debug (LOG_ASSEMBLY, "Mapped assembly store {}"sv, get_full_store_path ());
+ log_debug (LOG_ASSEMBLY, "Mapped assembly store %s", get_full_store_path ().c_str ());
}
diff --git a/src/native/clr/host/bridge-processing.cc b/src/native/clr/host/bridge-processing.cc
index b4ea077a081..0d06315c7bb 100644
--- a/src/native/clr/host/bridge-processing.cc
+++ b/src/native/clr/host/bridge-processing.cc
@@ -1,3 +1,5 @@
+#include
+
#include
#include
#include
@@ -6,20 +8,114 @@
using namespace xamarin::android;
-void BridgeProcessingShared::initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noexcept
+TemporaryPeerMap::TemporaryPeerMap (JNIEnv *jni_env, MarkCrossReferencesArgs *args) noexcept
+ : env{ jni_env },
+ cross_refs{ args }
+{
+ size_t map_capacity = 0;
+ for (size_t i = 0; i < cross_refs->ComponentCount; i++) {
+ const StronglyConnectedComponent &scc = cross_refs->Components [i];
+ abort_unless (!is_temporary_peer_index (scc.Count), "SCC count must not use the temporary peer marker bit");
+ if (scc.Count == 0) {
+ map_capacity++;
+ }
+ }
+
+ if (map_capacity == 0) {
+ return;
+ }
+
+ capacity = map_capacity;
+ peers = static_cast (std::calloc (capacity, sizeof (jobject)));
+ abort_unless (peers != nullptr, "Failed to allocate GC bridge temporary peer map");
+}
+
+TemporaryPeerMap::~TemporaryPeerMap () noexcept
+{
+ if (peers == nullptr) {
+ return;
+ }
+
+ for (size_t i = 0; i < count; i++) {
+ jobject temporary_peer = peers [i];
+ if (temporary_peer != nullptr) {
+ env->DeleteLocalRef (temporary_peer);
+ peers [i] = nullptr;
+ }
+ }
+
+ for (size_t i = 0; i < cross_refs->ComponentCount; i++) {
+ StronglyConnectedComponent &scc = cross_refs->Components [i];
+ if (is_temporary_peer_index (scc.Count)) {
+ scc.Count = 0;
+ }
+ }
+
+ count = 0;
+ std::free (peers);
+ peers = nullptr;
+ capacity = 0;
+}
+
+void TemporaryPeerMap::initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noexcept
{
abort_if_invalid_pointer_argument (env, "env");
abort_if_invalid_pointer_argument (runtimeClass, "runtimeClass");
- GCUserPeer_class = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "mono_android_GCUserPeer", true);
- GCUserPeer_ctor = env->GetMethodID (GCUserPeer_class, "", "()V");
+ peer_class = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "mono_android_GCUserPeer", true);
+ abort_unless (peer_class != nullptr, "Failed to load mono.android.GCUserPeer!");
+
+ peer_ctor = env->GetMethodID (peer_class, "", "()V");
+ abort_unless (peer_ctor != nullptr, "Failed to load mono.android.GCUserPeer constructor!");
+}
+
+void TemporaryPeerMap::add (StronglyConnectedComponent &scc) noexcept
+{
+ abort_unless (peers != nullptr, "Temporary peer map must not be null");
+ abort_unless (count < capacity, "Temporary peer map must not be full");
+
+ jobject temporary_peer = env->NewObject (peer_class, peer_ctor);
+ abort_unless (temporary_peer != nullptr, "Failed to create GC bridge temporary peer");
+
+ size_t temporary_peer_index = count++;
+ peers [temporary_peer_index] = temporary_peer;
+ scc.Count = encode_temporary_peer_index (temporary_peer_index);
+}
+
+bool TemporaryPeerMap::has_temporary_peer (const StronglyConnectedComponent &scc) const noexcept
+{
+ return is_temporary_peer_index (scc.Count);
+}
+
+jobject TemporaryPeerMap::get (const StronglyConnectedComponent &scc) const noexcept
+{
+ size_t temporary_peer_index = decode_temporary_peer_index (scc.Count);
+ abort_unless (temporary_peer_index < count, "Temporary peer index must be in range");
+
+ return peers [temporary_peer_index];
+}
+
+bool TemporaryPeerMap::is_temporary_peer_index (size_t count) noexcept
+{
+ return (count & temporary_peer_index_sign_bit) != 0;
+}
+
+size_t TemporaryPeerMap::encode_temporary_peer_index (size_t index) noexcept
+{
+ abort_unless (!is_temporary_peer_index (index), "Temporary peer index is too large");
+ return ~index;
+}
- abort_unless (GCUserPeer_class != nullptr && GCUserPeer_ctor != nullptr, "Failed to load mono.android.GCUserPeer!");
+size_t TemporaryPeerMap::decode_temporary_peer_index (size_t count) noexcept
+{
+ abort_unless (is_temporary_peer_index (count), "Temporary peer index must be negative");
+ return ~count;
}
-BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args) noexcept
+BridgeProcessingShared::BridgeProcessingShared (MarkCrossReferencesArgs *args, const BridgeProcessingCallbacks *host_callbacks) noexcept
: env{ OSBridge::ensure_jnienv () },
- cross_refs{ args }
+ cross_refs{ args },
+ callbacks{ host_callbacks != nullptr ? *host_callbacks : BridgeProcessingCallbacks {} }
{
if (args == nullptr) [[unlikely]] {
Helpers::abort_application (LOG_GC, "Cross references argument is a NULL pointer"sv);
@@ -44,25 +140,9 @@ void BridgeProcessingShared::process () noexcept
void BridgeProcessingShared::prepare_for_java_collection () noexcept
{
- // Before looking at xrefs, scan the SCCs. During collection, an SCC has to behave like a
- // single object. If the number of objects in the SCC is anything other than 1, the SCC
- // must be doctored to mimic that one-object nature.
- for (size_t i = 0; i < cross_refs->ComponentCount; i++) {
- const StronglyConnectedComponent &scc = cross_refs->Components [i];
- prepare_scc_for_java_collection (i, scc);
- }
-
- // Add the cross scc refs
- for (size_t i = 0; i < cross_refs->CrossReferenceCount; i++) {
- const ComponentCrossReference &xref = cross_refs->CrossReferences [i];
- add_cross_reference (xref.SourceGroupIndex, xref.DestinationGroupIndex);
- }
-
- // With cross references processed, the temporary peer list can be released
- for (const auto& [scc, temporary_peer] : temporary_peers) {
- env->DeleteLocalRef (temporary_peer);
- }
+ prepare_sccs_and_cross_references_for_java_collection ();
+ // Temporary peer indexes have been reset, so SCC counts are safe to use normally again.
// Switch global to weak references
for (size_t i = 0; i < cross_refs->ComponentCount; i++) {
const StronglyConnectedComponent &scc = cross_refs->Components [i];
@@ -75,11 +155,32 @@ void BridgeProcessingShared::prepare_for_java_collection () noexcept
}
}
-void BridgeProcessingShared::prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc) noexcept
+void BridgeProcessingShared::prepare_sccs_and_cross_references_for_java_collection () noexcept
+{
+ TemporaryPeerMap temporary_peers { env, cross_refs };
+
+ // Before looking at xrefs, scan the SCCs. During collection, an SCC has to behave like a
+ // single object. If the number of objects in the SCC is anything other than 1, the SCC
+ // must be doctored to mimic that one-object nature.
+ for (size_t i = 0; i < cross_refs->ComponentCount; i++) {
+ const StronglyConnectedComponent &scc = cross_refs->Components [i];
+ prepare_scc_for_java_collection (i, scc, temporary_peers);
+ }
+
+ // Add the cross scc refs
+ for (size_t i = 0; i < cross_refs->CrossReferenceCount; i++) {
+ const ComponentCrossReference &xref = cross_refs->CrossReferences [i];
+ add_cross_reference (xref.SourceGroupIndex, xref.DestinationGroupIndex, temporary_peers);
+ }
+}
+
+void BridgeProcessingShared::prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc, TemporaryPeerMap &temporary_peers) noexcept
{
- // Count == 0 case: Some SCCs might have no IGCUserPeers associated with them, so we must create one
+ // Before looking at xrefs, scan the SCCs. During collection, an SCC has to behave like a
+ // single object. If the number of objects in the SCC is anything other than 1, the SCC
+ // must be doctored to mimic that one-object nature.
if (scc.Count == 0) {
- temporary_peers [scc_index] = env->NewObject (GCUserPeer_class, GCUserPeer_ctor);
+ temporary_peers.add (cross_refs->Components [scc_index]);
return;
}
@@ -93,14 +194,14 @@ void BridgeProcessingShared::prepare_scc_for_java_collection (size_t scc_index,
add_circular_references (scc);
}
-CrossReferenceTarget BridgeProcessingShared::select_cross_reference_target (size_t scc_index) noexcept
+CrossReferenceTarget BridgeProcessingShared::select_cross_reference_target (size_t scc_index, TemporaryPeerMap &temporary_peers) noexcept
{
const StronglyConnectedComponent &scc = cross_refs->Components [scc_index];
- if (scc.Count == 0) {
- const auto temporary_peer = temporary_peers.find (scc_index);
- abort_unless (temporary_peer != temporary_peers.end(), "Temporary peer must be found in the map");
- return { .is_temporary_peer = true, .temporary_peer = temporary_peer->second };
+ if (temporary_peers.has_temporary_peer (scc)) {
+ jobject temporary_peer = temporary_peers.get (scc);
+ abort_unless (temporary_peer != nullptr, "Temporary peer must not be null");
+ return { .is_temporary_peer = true, .temporary_peer = temporary_peer };
}
abort_unless (scc.Contexts [0] != nullptr, "SCC must have at least one context");
@@ -142,10 +243,10 @@ void BridgeProcessingShared::add_circular_references (const StronglyConnectedCom
}
}
-void BridgeProcessingShared::add_cross_reference (size_t source_index, size_t dest_index) noexcept
+void BridgeProcessingShared::add_cross_reference (size_t source_index, size_t dest_index, TemporaryPeerMap &temporary_peers) noexcept
{
- CrossReferenceTarget from = select_cross_reference_target (source_index);
- CrossReferenceTarget to = select_cross_reference_target (dest_index);
+ CrossReferenceTarget from = select_cross_reference_target (source_index, temporary_peers);
+ CrossReferenceTarget to = select_cross_reference_target (dest_index, temporary_peers);
if (add_reference (from.get_handle(), to.get_handle())) {
from.mark_refs_added_if_needed ();
@@ -219,6 +320,24 @@ void BridgeProcessingShared::clear_references (jobject handle) noexcept
env->CallVoidMethod (handle, clear_method_id);
}
+bool BridgeProcessingShared::maybe_call_gc_user_peerable_add_managed_reference (JNIEnv *jni_env, jobject from, jobject to) noexcept
+{
+ if (callbacks.maybe_call_gc_user_peerable_add_managed_reference == nullptr) {
+ return false;
+ }
+
+ return callbacks.maybe_call_gc_user_peerable_add_managed_reference (jni_env, from, to);
+}
+
+bool BridgeProcessingShared::maybe_call_gc_user_peerable_clear_managed_references (JNIEnv *jni_env, jobject handle) noexcept
+{
+ if (callbacks.maybe_call_gc_user_peerable_clear_managed_references == nullptr) {
+ return false;
+ }
+
+ return callbacks.maybe_call_gc_user_peerable_clear_managed_references (jni_env, handle);
+}
+
void BridgeProcessingShared::take_global_ref (HandleContext &context) noexcept
{
abort_unless (context.control_block != nullptr, "Control block must not be null");
@@ -317,7 +436,7 @@ void CrossReferenceTarget::mark_refs_added_if_needed () noexcept
[[gnu::always_inline]]
void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]] jclass java_class) noexcept
{
- log_error (LOG_DEFAULT, "Failed to find monodroidAddReferences method");
+ (log_error) (LOG_DEFAULT, "Failed to find monodroidAddReferences method");
#if DEBUG
abort_if_invalid_pointer_argument (java_class, "java_class");
if (!Logger::gc_spew_enabled ()) [[likely]] {
@@ -325,7 +444,7 @@ void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]]
}
char *class_name = Host::get_java_class_name_for_TypeManager (java_class);
- log_error (LOG_GC, "Missing monodroidAddReferences method for object of class {}", optional_string (class_name));
+ (log_error) (LOG_GC, "Missing monodroidAddReferences method for object of class %s", optional_string (class_name));
free (class_name);
#endif
}
@@ -333,7 +452,7 @@ void BridgeProcessingShared::log_missing_add_references_method ([[maybe_unused]]
[[gnu::always_inline]]
void BridgeProcessingShared::log_missing_clear_references_method ([[maybe_unused]] jclass java_class) noexcept
{
- log_error (LOG_DEFAULT, "Failed to find monodroidClearReferences method");
+ (log_error) (LOG_DEFAULT, "Failed to find monodroidClearReferences method");
#if DEBUG
abort_if_invalid_pointer_argument (java_class, "java_class");
if (!Logger::gc_spew_enabled ()) [[likely]] {
@@ -341,7 +460,7 @@ void BridgeProcessingShared::log_missing_clear_references_method ([[maybe_unused
}
char *class_name = Host::get_java_class_name_for_TypeManager (java_class);
- log_error (LOG_GC, "Missing monodroidClearReferences method for object of class {}", optional_string (class_name));
+ (log_error) (LOG_GC, "Missing monodroidClearReferences method for object of class %s", optional_string (class_name));
free (class_name);
#endif
}
@@ -364,10 +483,7 @@ void BridgeProcessingShared::log_weak_to_gref (jobject weak, jobject handle) noe
return;
}
- OSBridge::_monodroid_gref_log (
- std::format ("take_global_ref wref={:#x} -> handle={:#x}\n"sv,
- reinterpret_cast (weak),
- reinterpret_cast (handle)).data ());
+ OSBridge::_monodroid_gref_log ("take_global_ref wref=%p -> handle=%p\n", reinterpret_cast(weak), reinterpret_cast(handle));
}
[[gnu::always_inline]]
@@ -377,8 +493,7 @@ void BridgeProcessingShared::log_weak_ref_collected (jobject weak) noexcept
return;
}
- OSBridge::_monodroid_gref_log (
- std::format ("handle {:#x}/W; was collected by a Java GC"sv, reinterpret_cast (weak)).data ());
+ OSBridge::_monodroid_gref_log ("handle %p/W; was collected by a Java GC", reinterpret_cast(weak));
}
[[gnu::always_inline]]
@@ -388,7 +503,7 @@ void BridgeProcessingShared::log_take_weak_global_ref (jobject handle) noexcept
return;
}
- OSBridge::_monodroid_gref_log (std::format ("take_weak_global_ref handle={:#x}\n"sv, reinterpret_cast (handle)).data ());
+ OSBridge::_monodroid_gref_log ("take_weak_global_ref handle=%p\n", reinterpret_cast(handle));
}
[[gnu::always_inline]]
@@ -449,5 +564,5 @@ void BridgeProcessingShared::log_gc_summary () noexcept
}
}
- log_info (LOG_GC, "GC cleanup summary: {} objects tested - resurrecting {}.", total, alive);
+ log_info_fmt (LOG_GC, "GC cleanup summary: %zu objects tested - resurrecting %zu.", total, alive);
}
diff --git a/src/native/clr/host/fastdev-assemblies.cc b/src/native/clr/host/fastdev-assemblies.cc
index dea16523aaf..00c351d515e 100644
--- a/src/native/clr/host/fastdev-assemblies.cc
+++ b/src/native/clr/host/fastdev-assemblies.cc
@@ -5,6 +5,7 @@
#include
#include
+#include
#include
#include
@@ -18,9 +19,9 @@ auto FastDevAssemblies::open_assembly (std::string_view const& name, int64_t &si
{
size = 0;
- std::string const& override_dir_path = AndroidSystem::get_primary_override_dir ();
+ const char *override_dir_path = AndroidSystem::get_primary_override_dir ();
if (!Util::dir_exists (override_dir_path)) [[unlikely]] {
- log_debug (LOG_ASSEMBLY, "Override directory '{}' does not exist"sv, override_dir_path);
+ log_debug (LOG_ASSEMBLY, "Override directory '%s' does not exist", optional_string (override_dir_path));
return nullptr;
}
@@ -29,9 +30,9 @@ auto FastDevAssemblies::open_assembly (std::string_view const& name, int64_t &si
if (override_dir_fd < 0) [[unlikely]] {
std::lock_guard dir_lock { override_dir_lock };
if (override_dir_fd < 0) [[likely]] {
- override_dir = opendir (override_dir_path.c_str ());
+ override_dir = opendir (override_dir_path);
if (override_dir == nullptr) [[unlikely]] {
- log_warn (LOG_ASSEMBLY, "Failed to open override dir '{}'. {}"sv, override_dir_path, strerror (errno));
+ log_warn (LOG_ASSEMBLY, "Failed to open override dir '%s'. %s", optional_string (override_dir_path), strerror (errno));
return nullptr;
}
override_dir_fd = dirfd (override_dir);
@@ -40,20 +41,21 @@ auto FastDevAssemblies::open_assembly (std::string_view const& name, int64_t &si
log_debug (
LOG_ASSEMBLY,
- "Attempting to load FastDev assembly '{}' from override directory '{}'"sv,
- name,
- override_dir_path
+ "Attempting to load FastDev assembly '%.*s' from override directory '%s'",
+ static_cast(name.length ()),
+ name.data (),
+ optional_string (override_dir_path)
);
if (!Util::file_exists (override_dir_fd, name)) {
- log_warn (LOG_ASSEMBLY, "FastDev assembly '{}' not found."sv, name);
+ log_warn (LOG_ASSEMBLY, "FastDev assembly '%.*s' not found.", static_cast(name.length ()), name.data ());
return nullptr;
}
- log_debug (LOG_ASSEMBLY, "Found FastDev assembly '{}'"sv, name);
+ log_debug (LOG_ASSEMBLY, "Found FastDev assembly '%.*s'", static_cast(name.length ()), name.data ());
auto file_size = Util::get_file_size_at (override_dir_fd, name);
if (!file_size) [[unlikely]] {
- log_warn (LOG_ASSEMBLY, "Unable to determine FastDev assembly '{}' file size"sv, name);
+ log_warn (LOG_ASSEMBLY, "Unable to determine FastDev assembly '%.*s' file size", static_cast(name.length ()), name.data ());
return nullptr;
}
@@ -74,8 +76,9 @@ auto FastDevAssemblies::open_assembly (std::string_view const& name, int64_t &si
if (asm_fd < 0) {
log_warn (
LOG_ASSEMBLY,
- "Failed to open FastDev assembly '{}' for reading. {}"sv,
- name,
+ "Failed to open FastDev assembly '%.*s' for reading. %s",
+ static_cast(name.length ()),
+ name.data (),
strerror (errno)
);
@@ -99,15 +102,16 @@ auto FastDevAssemblies::open_assembly (std::string_view const& name, int64_t &si
log_warn (
LOG_ASSEMBLY,
- "Failed to read FastDev assembly '{}' data. {}"sv,
- name,
+ "Failed to read FastDev assembly '%.*s' data. %s",
+ static_cast(name.length ()),
+ name.data (),
strerror (errno)
);
size = 0;
return nullptr;
}
- log_debug (LOG_ASSEMBLY, "Read {} bytes of FastDev assembly '{}'"sv, nread, name);
+ log_debug (LOG_ASSEMBLY, "Read %zd bytes of FastDev assembly '%.*s'", nread, static_cast(name.length ()), name.data ());
return reinterpret_cast(buffer);
}
diff --git a/src/native/clr/host/gc-bridge.cc b/src/native/clr/host/gc-bridge.cc
index efd8f502ae4..6d2077ea7ce 100644
--- a/src/native/clr/host/gc-bridge.cc
+++ b/src/native/clr/host/gc-bridge.cc
@@ -1,3 +1,7 @@
+#include
+#include
+#include
+
#include
#include
#include
@@ -31,7 +35,7 @@ void GCBridge::initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noe
abort_if_invalid_pointer_argument (env, "env");
abort_if_invalid_pointer_argument (runtimeClass, "runtimeClass");
- BridgeProcessing::initialize_on_runtime_init (env, runtimeClass);
+ TemporaryPeerMap::initialize_on_runtime_init (env, runtimeClass);
}
void GCBridge::trigger_java_gc (JNIEnv *env) noexcept
@@ -45,7 +49,7 @@ void GCBridge::trigger_java_gc (JNIEnv *env) noexcept
env->ExceptionDescribe ();
env->ExceptionClear ();
- log_error (LOG_DEFAULT, "Java GC failed");
+ (log_error) (LOG_DEFAULT, "Java GC failed");
}
void GCBridge::mark_cross_references (MarkCrossReferencesArgs *args) noexcept
@@ -55,8 +59,34 @@ void GCBridge::mark_cross_references (MarkCrossReferencesArgs *args) noexcept
abort_unless (args->CrossReferences != nullptr || args->CrossReferenceCount == 0, "CrossReferences must not be null if CrossReferenceCount is greater than 0");
log_mark_cross_references_args_if_enabled (args);
- shared_args.store (args);
- shared_args_semaphore.release ();
+ send_and_signal (args);
+}
+
+void GCBridge::send_and_signal (MarkCrossReferencesArgs *args) noexcept
+{
+ __atomic_store_n (&shared_args, args, __ATOMIC_RELEASE);
+ int ret = sem_post (&shared_args_semaphore);
+ abort_unless (ret == 0, "Failed to release GC bridge semaphore");
+}
+
+auto GCBridge::enter_bridge_processing () noexcept -> MarkCrossReferencesArgs*
+{
+ // wait until mark cross references args are set by the GC callback
+ int ret;
+ do {
+ ret = sem_wait (&shared_args_semaphore);
+ } while (ret == -1 && errno == EINTR);
+ abort_unless (ret == 0, "Failed to acquire GC bridge semaphore");
+
+ return __atomic_load_n (&shared_args, __ATOMIC_ACQUIRE);
+}
+
+void GCBridge::process_bridge_args (MarkCrossReferencesArgs *args) noexcept
+{
+ // Bridge processing temporarily mutates args while it owns them. Make sure any
+ // changes are reverted before returning args to the GC bridge finish callback.
+ BridgeProcessing bridge_processing {args};
+ bridge_processing.process ();
}
void GCBridge::bridge_processing () noexcept
@@ -65,19 +95,19 @@ void GCBridge::bridge_processing () noexcept
abort_unless (bridge_processing_finished_callback != nullptr, "GC bridge processing finished callback is not set");
while (true) {
- // wait until mark cross references args are set by the GC callback
- shared_args_semaphore.acquire ();
- MarkCrossReferencesArgs *args = shared_args.load ();
-
+ MarkCrossReferencesArgs *args = enter_bridge_processing ();
bridge_processing_started_callback (args);
-
- BridgeProcessing bridge_processing {args};
- bridge_processing.process ();
-
+ process_bridge_args (args);
bridge_processing_finished_callback (java_marshal_value_manager_handle, args);
}
}
+auto GCBridge::bridge_processing_thread_entry ([[maybe_unused]] void *arg) noexcept -> void*
+{
+ bridge_processing ();
+ return nullptr;
+}
+
[[gnu::always_inline]]
void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArgs *args) noexcept
{
@@ -85,13 +115,13 @@ void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArg
return;
}
- log_info (LOG_GC, "cross references callback invoked with {} sccs and {} xrefs.", args->ComponentCount, args->CrossReferenceCount);
+ log_info_fmt (LOG_GC, "cross references callback invoked with %zu sccs and %zu xrefs.", args->ComponentCount, args->CrossReferenceCount);
JNIEnv *env = OSBridge::ensure_jnienv ();
for (size_t i = 0; i < args->ComponentCount; ++i) {
const StronglyConnectedComponent &scc = args->Components [i];
- log_info (LOG_GC, "group {} with {} objects", i, scc.Count);
+ log_info_fmt (LOG_GC, "group %zu with %zu objects", i, scc.Count);
for (size_t j = 0; j < scc.Count; ++j) {
log_handle_context (env, scc.Contexts [j]);
}
@@ -104,7 +134,7 @@ void GCBridge::log_mark_cross_references_args_if_enabled (MarkCrossReferencesArg
for (size_t i = 0; i < args->CrossReferenceCount; ++i) {
size_t source_index = args->CrossReferences [i].SourceGroupIndex;
size_t dest_index = args->CrossReferences [i].DestinationGroupIndex;
- log_info_nocheck_fmt (LOG_GC, "xref [{}] {} -> {}", i, source_index, dest_index);
+ log_info_fmt (LOG_GC, "xref [%zu] %zu -> %zu", i, source_index, dest_index);
}
}
@@ -118,9 +148,9 @@ void GCBridge::log_handle_context (JNIEnv *env, HandleContext *ctx) noexcept
jclass java_class = env->GetObjectClass (handle);
if (java_class != nullptr) {
char *class_name = Host::get_java_class_name_for_TypeManager (java_class);
- log_info (LOG_GC, "gref {:#x} [{}]", reinterpret_cast (handle), class_name);
+ log_info_fmt (LOG_GC, "gref %p [%s]", reinterpret_cast(handle), optional_string (class_name));
free (class_name);
} else {
- log_info (LOG_GC, "gref {:#x} [unknown class]", reinterpret_cast (handle));
+ log_info_fmt (LOG_GC, "gref %p [unknown class]", reinterpret_cast(handle));
}
}
diff --git a/src/native/clr/host/host-shared.cc b/src/native/clr/host/host-shared.cc
index 5015a7a7a34..5909283a498 100644
--- a/src/native/clr/host/host-shared.cc
+++ b/src/native/clr/host/host-shared.cc
@@ -1,3 +1,5 @@
+#include
+
#include
#include
@@ -12,13 +14,13 @@ auto HostCommon::get_java_class_name_for_TypeManager (jclass klass) noexcept ->
JNIEnv *env = OSBridge::ensure_jnienv ();
jstring name = reinterpret_cast (env->CallObjectMethod (klass, Class_getName));
if (name == nullptr) {
- log_error (LOG_DEFAULT, "Failed to obtain Java class name for object at {:p}", reinterpret_cast(klass));
+ log_write_fmt (LOG_DEFAULT, LogLevel::Error, "Failed to obtain Java class name for object at %p", reinterpret_cast(klass));
return nullptr;
}
const char *mutf8 = env->GetStringUTFChars (name, nullptr);
if (mutf8 == nullptr) {
- log_error (LOG_DEFAULT, "Failed to convert Java class name to UTF8 (out of memory?)"sv);
+ log_write (LOG_DEFAULT, LogLevel::Error, "Failed to convert Java class name to UTF8 (out of memory?)");
env->DeleteLocalRef (name);
return nullptr;
}
@@ -34,4 +36,4 @@ auto HostCommon::get_java_class_name_for_TypeManager (jclass klass) noexcept ->
}
return ret;
-}
\ No newline at end of file
+}
diff --git a/src/native/clr/host/host.cc b/src/native/clr/host/host.cc
index 3e84ef930d8..f98b3180f92 100644
--- a/src/native/clr/host/host.cc
+++ b/src/native/clr/host/host.cc
@@ -4,6 +4,7 @@
#include
#include
#include
+#include
#include
#include
@@ -35,14 +36,14 @@ using namespace xamarin::android;
void Host::clr_error_writer (const char *message) noexcept
{
- log_error (LOG_DEFAULT, "CLR error: {}", optional_string (message));
+ log_error (LOG_DEFAULT, "CLR error: %s", optional_string (message));
}
size_t Host::clr_get_runtime_property (const char *key, char *value_buffer, size_t value_buffer_size, [[maybe_unused]] void *contract_context) noexcept
{
// NOTE: this code was tested locally, but it's **not** used by CoreCLR yet, so there's been no
// "live" testing.
- log_debug (LOG_DEFAULT, "clr_get_runtime_property (\"{}\"...)"sv, optional_string (key));
+ log_debug (LOG_DEFAULT, "clr_get_runtime_property (\"%s\"...)", optional_string (key));
if (application_config.number_of_runtime_properties == 0) [[unlikely]] {
log_debug (LOG_DEFAULT, "No runtime properties defined"sv);
return 0;
@@ -52,7 +53,7 @@ size_t Host::clr_get_runtime_property (const char *key, char *value_buffer, size
if (key == nullptr || value_buffer == nullptr || value_buffer_size <= 1) [[unlikely]] {
log_warn (
LOG_DEFAULT,
- "runtime property retrieval API called with invalid arguments. key == {:p}; value_buffer == {:p}; value_buffer_size == {}"sv,
+ "runtime property retrieval API called with invalid arguments. key == %p; value_buffer == %p; value_buffer_size == %zu",
static_cast(key),
static_cast(value_buffer),
value_buffer_size
@@ -66,7 +67,7 @@ size_t Host::clr_get_runtime_property (const char *key, char *value_buffer, size
auto less_than = [](RuntimePropertyIndexEntry const& entry, hash_t key) -> bool { return entry.key_hash < key; };
ssize_t idx = Search::binary_search (key_hash, runtime_property_index, application_config.number_of_runtime_properties);
if (idx < 0) {
- log_debug (LOG_DEFAULT, "Runtime property '{}' not found"sv, key);
+ log_debug (LOG_DEFAULT, "Runtime property '%s' not found", key);
return 0;
}
@@ -77,7 +78,7 @@ size_t Host::clr_get_runtime_property (const char *key, char *value_buffer, size
if (prop.value_size > value_buffer_size) {
log_warn (
LOG_DEFAULT,
- "Value of property '{}' is longer than available buffer space. Need {}b, available {}b"sv,
+ "Value of property '%s' is longer than available buffer space. Need %u b, available %zu b",
key,
prop.value_size,
value_buffer_size
@@ -91,7 +92,7 @@ size_t Host::clr_get_runtime_property (const char *key, char *value_buffer, size
bool Host::clr_external_assembly_probe (const char *path, void **data_start, int64_t *size) noexcept
{
// TODO: `path` might be a full path, make sure it isn't
- log_debug (LOG_DEFAULT, "clr_external_assembly_probe (\"{}\"...)"sv, path);
+ log_debug (LOG_DEFAULT, "clr_external_assembly_probe (\"%s\"...)", optional_string (path));
if (data_start == nullptr || size == nullptr) {
return false; // TODO: abort instead?
}
@@ -108,11 +109,11 @@ bool Host::clr_external_assembly_probe (const char *path, void **data_start, int
log_debug (
LOG_ASSEMBLY,
- "Assembly '{}' data {}mapped ({:p}, {} bytes)",
+ "Assembly '%s' data %smapped (%p, %lld bytes)",
optional_string (name),
- data_start == nullptr ? "not "sv : ""sv,
+ data_start == nullptr ? "not " : "",
data_start,
- size
+ static_cast(size)
);
return data_start != nullptr && size > 0;
@@ -126,7 +127,7 @@ bool Host::clr_external_assembly_probe (const char *path, void **data_start, int
log_warn (
LOG_ASSEMBLY,
- "Assembly '{}' not found in FastDev override directory. Attempting to load from assembly store"sv,
+ "Assembly '%s' not found in FastDev override directory. Attempting to load from assembly store",
optional_string (path)
);
}
@@ -138,11 +139,11 @@ bool Host::clr_external_assembly_probe (const char *path, void **data_start, int
auto Host::zip_scan_callback (std::string_view const& apk_path, int apk_fd, dynamic_local_string const& entry_name, uint32_t offset, uint32_t size) -> bool
{
- log_debug (LOG_ASSEMBLY, "zip entry: {}"sv, entry_name.get ());
+ log_debug (LOG_ASSEMBLY, "zip entry: %s", entry_name.get ());
if (!found_assembly_store) {
found_assembly_store = Zip::assembly_store_file_path.compare (0, entry_name.length (), entry_name.get ()) == 0;
if (found_assembly_store) {
- log_debug (LOG_ASSEMBLY, "Found assembly store in '{}': {}"sv, apk_path, Zip::assembly_store_file_path);
+ log_debug (LOG_ASSEMBLY, "Found assembly store in '%.*s': %.*s", static_cast(apk_path.length ()), apk_path.data (), static_cast(Zip::assembly_store_file_path.length ()), Zip::assembly_store_file_path.data ());
AssemblyStore::map (apk_fd, apk_path, Zip::assembly_store_file_path, offset, size);
return false; // This will make the scanner keep the APK open
}
@@ -152,10 +153,10 @@ auto Host::zip_scan_callback (std::string_view const& apk_path, int apk_fd, dyna
return false;
}
- log_debug (LOG_ASSEMBLY, "Found shared library in '{}': {}"sv, apk_path, entry_name.get ());
+ log_debug (LOG_ASSEMBLY, "Found shared library in '%.*s': %s", static_cast(apk_path.length ()), apk_path.data (), entry_name.get ());
std::string_view lib_name { entry_name.get () + Zip::lib_prefix.length () };
hash_t name_hash = xxhash::hash (lib_name.data (), lib_name.length ());
- log_debug (LOG_ASSEMBLY, "Library name is: {}; hash == 0x{:x}", lib_name, name_hash);
+ log_debug (LOG_ASSEMBLY, "Library name is: %.*s; hash == 0x%zx", static_cast(lib_name.length ()), lib_name.data (), static_cast(name_hash));
DSOApkEntry *apk_entry = MonodroidDl::find_dso_apk_entry (name_hash);
if (apk_entry == nullptr) {
@@ -172,7 +173,7 @@ auto Host::zip_scan_callback (std::string_view const& apk_path, int apk_fd, dyna
void Host::scan_filesystem_for_assemblies_and_libraries () noexcept
{
std::string const& native_lib_dir = AndroidSystem::get_native_libraries_dir ();
- log_debug (LOG_ASSEMBLY, "Looking for assemblies in '{}'"sv, native_lib_dir);
+ log_debug (LOG_ASSEMBLY, "Looking for assemblies in '%s'", native_lib_dir.c_str ());
DIR *lib_dir = opendir (native_lib_dir.c_str ());
if (lib_dir == nullptr) [[unlikely]] {
@@ -203,7 +204,7 @@ void Host::scan_filesystem_for_assemblies_and_libraries () noexcept
dirent *cur = readdir (lib_dir);
if (cur == nullptr) {
if (errno != 0) {
- log_warn (LOG_ASSEMBLY, "Failed to open a directory entry from '{}': {}"sv, native_lib_dir, std::strerror (errno));
+ log_warn (LOG_ASSEMBLY, "Failed to open a directory entry from '%s': %s", native_lib_dir.c_str (), std::strerror (errno));
continue; // No harm, keep going
}
break; // we're done
@@ -220,7 +221,7 @@ void Host::scan_filesystem_for_assemblies_and_libraries () noexcept
continue;
}
- log_debug (LOG_ASSEMBLY, "Found assembly store in '{}/{}'"sv, native_lib_dir, Constants::assembly_store_file_name);
+ log_debug (LOG_ASSEMBLY, "Found assembly store in '%s/%.*s'", native_lib_dir.c_str (), static_cast(Constants::assembly_store_file_name.length ()), Constants::assembly_store_file_name.data ());
int store_fd = openat (dir_fd, cur->d_name, O_RDONLY);
if (store_fd < 0) {
Helpers::abort_application (
@@ -312,10 +313,13 @@ auto Host::create_delegate (
&delegate
);
log_debug (LOG_ASSEMBLY,
- "{}@{}.{} delegate creation result == {:x}; delegate == {:p}"sv,
- assembly_name,
- type_name,
- method_name,
+ "%.*s@%.*s.%.*s delegate creation result == %x; delegate == %p",
+ static_cast(assembly_name.length ()),
+ assembly_name.data (),
+ static_cast(type_name.length ()),
+ type_name.data (),
+ static_cast(method_name.length ()),
+ method_name.data (),
static_cast(hr),
delegate
);
@@ -344,7 +348,7 @@ void Host::preload_jni_libraries () noexcept
return;
}
- log_debug (LOG_ASSEMBLY, "DSO jni preloads index stride == {}", dso_jni_preloads_idx_stride);
+ log_debug (LOG_ASSEMBLY, "DSO jni preloads index stride == %u", dso_jni_preloads_idx_stride);
if ((dso_jni_preloads_idx_count % dso_jni_preloads_idx_stride) != 0) [[unlikely]] {
Helpers::abort_application (
@@ -364,11 +368,12 @@ void Host::preload_jni_libraries () noexcept
log_debug (
LOG_ASSEMBLY,
- "Preloading JNI shared library: {} (entry's index: {}; real name hash: {:x}; name hash: {:x})",
- dso_name,
+ "Preloading JNI shared library: %.*s (entry's index: %zu; real name hash: %zx; name hash: %zx)",
+ static_cast(dso_name.length ()),
+ dso_name.data (),
entry_index,
- entry.real_name_hash,
- entry.hash
+ static_cast(entry.real_name_hash),
+ static_cast(entry.hash)
);
void *handle = MonodroidDl::monodroid_dlopen (&entry, dso_name, RTLD_NOW);
@@ -381,9 +386,10 @@ void Host::preload_jni_libraries () noexcept
log_debug (
LOG_ASSEMBLY,
- "Putting JNI library handle in alias entry at index {}: {}",
+ "Putting JNI library handle in alias entry at index %zu: %.*s",
entry_alias_index,
- entry_alias_name
+ static_cast(entry_alias_name.length ()),
+ entry_alias_name.data ()
);
entry_alias.handle = handle;
}
@@ -519,7 +525,7 @@ void Host::Java_mono_android_Runtime_initInternal (
init.grefIGCUserPeer = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "mono_android_IGCUserPeer"sv, true);
init.grefGCUserPeerable = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "net_dot_jni_GCUserPeerable"sv, true);
- log_info (LOG_GC, "GREF GC Threshold: {}"sv, init.grefGcThreshold);
+ log_info (LOG_GC, "GREF GC Threshold: %d", init.grefGcThreshold);
OSBridge::initialize_on_runtime_init (env, runtimeClass);
GCBridge::initialize_on_runtime_init (env, runtimeClass);
@@ -528,7 +534,7 @@ void Host::Java_mono_android_Runtime_initInternal (
internal_timing.start_event (TimingEventKind::NativeToManagedTransition);
}
- log_debug (LOG_ASSEMBLY, "Creating UCO delegate to {}.Initialize"sv, Constants::JNIENVINIT_FULL_TYPE_NAME);
+ log_debug (LOG_ASSEMBLY, "Creating UCO delegate to %.*s.Initialize", static_cast(Constants::JNIENVINIT_FULL_TYPE_NAME.length ()), Constants::JNIENVINIT_FULL_TYPE_NAME.data ());
void *delegate = nullptr;
delegate = FastTiming::time_call ("create_delegate for Initialize"sv, create_delegate, Constants::MONO_ANDROID_ASSEMBLY_NAME, Constants::JNIENVINIT_FULL_TYPE_NAME, "Initialize"sv);
auto initialize = reinterpret_cast (delegate);
@@ -573,7 +579,7 @@ void Host::Java_mono_android_Runtime_register (JNIEnv *env, jstring managedType,
dynamic_local_string managed_type_name;
const char *mt_ptr = env->GetStringUTFChars (managedType, nullptr);
managed_type_name.assign (mt_ptr, strlen (mt_ptr));
- log_debug (LOG_ASSEMBLY, "Registering type: '{}'"sv, managed_type_name.get ());
+ log_debug (LOG_ASSEMBLY, "Registering type: '%s'", managed_type_name.get ());
env->ReleaseStringUTFChars (managedType, mt_ptr);
// TODO: must attach thread to the runtime here
diff --git a/src/native/clr/host/internal-pinvokes-shared.cc b/src/native/clr/host/internal-pinvokes-shared.cc
index b828fdcfe15..9d19ce3d23f 100644
--- a/src/native/clr/host/internal-pinvokes-shared.cc
+++ b/src/native/clr/host/internal-pinvokes-shared.cc
@@ -57,30 +57,36 @@ void monodroid_log (LogLevel level, LogCategories category, const char *message)
switch (level) {
case LogLevel::Verbose:
case LogLevel::Debug:
- log_debug_nocheck (category, std::string_view { message });
+ if ((log_categories & category) != 0) {
+ log_write (category, LogLevel::Debug, message);
+ }
break;
case LogLevel::Info:
- log_info_nocheck (category, std::string_view { message });
+ if ((log_categories & category) != 0) {
+ log_write (category, LogLevel::Info, message);
+ }
break;
case LogLevel::Warn:
case LogLevel::Silent: // warn is always printed
- log_warn (category, std::string_view { message });
+ log_write (category, LogLevel::Warn, message);
break;
case LogLevel::Error:
- log_error (category, std::string_view { message });
+ log_write (category, LogLevel::Error, message);
break;
case LogLevel::Fatal:
- log_fatal (category, std::string_view { message });
+ log_write (category, LogLevel::Fatal, message);
break;
default:
case LogLevel::Unknown:
case LogLevel::Default:
- log_info_nocheck (category, std::string_view { message });
+ if ((log_categories & category) != 0) {
+ log_write (category, LogLevel::Info, message);
+ }
break;
}
}
diff --git a/src/native/clr/host/os-bridge.cc b/src/native/clr/host/os-bridge.cc
index cc2122e317e..e089afd767e 100644
--- a/src/native/clr/host/os-bridge.cc
+++ b/src/native/clr/host/os-bridge.cc
@@ -1,4 +1,7 @@
-#include
+#include
+#include
+#include
+#include
#include
#include
@@ -8,6 +11,46 @@
using namespace xamarin::android;
+namespace {
+ constexpr size_t LOG_LINE_BUFFER_SIZE = 512;
+
+ void write_logcat_line (LogCategories category, const char *line, size_t line_len) noexcept
+ {
+ char local_buffer[LOG_LINE_BUFFER_SIZE];
+ char *buffer = local_buffer;
+
+ if (line_len >= sizeof (local_buffer)) {
+ buffer = static_cast(malloc (line_len + 1));
+ if (buffer == nullptr) {
+ log_write (category, LogLevel::Debug, "");
+ return;
+ }
+ }
+
+ memcpy (buffer, line, line_len);
+ buffer[line_len] = '\0';
+ log_write (category, LogLevel::Debug, buffer);
+
+ if (buffer != local_buffer) {
+ free (buffer);
+ }
+ }
+
+ void write_gref_message (const char *message, FILE *gref_log) noexcept
+ {
+ if (Logger::gref_to_logcat ()) {
+ log_write (LOG_GREF, LogLevel::Debug, optional_string (message));
+ }
+
+ if (gref_log == nullptr) {
+ return;
+ }
+
+ fputs (optional_string (message), gref_log);
+ fflush (gref_log);
+ }
+}
+
void OSBridge::initialize_on_onload (JavaVM *vm, JNIEnv *env) noexcept
{
abort_if_invalid_pointer_argument (env, "env");
@@ -90,50 +133,66 @@ auto OSBridge::_monodroid_weak_gref_dec () noexcept -> int
void OSBridge::_write_stack_trace (FILE *to, const char *const from, LogCategories category) noexcept
{
if (from == nullptr) [[unlikely]] {
- log_warn (category, "Unable to write stack trace, managed runtime passed a NULL string.");
+ log_write (category, LogLevel::Warn, "Unable to write stack trace, managed runtime passed a NULL string.");
return;
}
- const std::string_view trace { from };
- if (trace.empty ()) [[unlikely]] {
- log_warn (category, "Empty stack trace passed by the managed runtime.");
+ if (*from == '\0') [[unlikely]] {
+ log_write (category, LogLevel::Warn, "Empty stack trace passed by the managed runtime.");
return;
}
- for (const auto segment : std::views::split (trace, '\n')) {
- const std::string_view line { segment };
+ const char *line = from;
+ while (line != nullptr && *line != '\0') {
+ const char *newline = strchr (line, '\n');
+ size_t line_len = newline == nullptr ? strlen (line) : static_cast(newline - line);
if ((category == LOG_GREF && Logger::gref_to_logcat ()) ||
(category == LOG_LREF && Logger::lref_to_logcat ())) {
- log_debug (category, "{}"sv, line);
+ write_logcat_line (category, line, line_len);
}
- if (to == nullptr) {
- continue;
+ if (to != nullptr) {
+ fwrite (line, sizeof (char), line_len, to);
+ fputc ('\n', to);
+ fflush (to);
}
- fwrite (line.data (), sizeof (std::string_view::value_type), line.length (), to);
- fputc ('\n', to);
- fflush (to);
+ if (newline == nullptr) {
+ break;
+ }
+ line = newline + 1;
}
}
void OSBridge::_monodroid_gref_log (const char *message) noexcept
{
- if (Logger::gref_to_logcat ()) {
- log_debug (LOG_GREF, "{}"sv, optional_string (message));
+ FILE *gref_log = Logger::gref_log ();
+ if (!Logger::gref_to_logcat () && gref_log == nullptr) {
+ return;
}
- if (Logger::gref_log () == nullptr) {
+ write_gref_message (message, gref_log);
+}
+
+void OSBridge::_monodroid_gref_log_fmt (const char *format, ...) noexcept
+{
+ FILE *gref_log = Logger::gref_log ();
+ if (!Logger::gref_to_logcat () && gref_log == nullptr) {
return;
}
- fprintf (Logger::gref_log (), "%s", optional_string (message));
- fflush (Logger::gref_log ());
+ char message[LOG_LINE_BUFFER_SIZE];
+ va_list args;
+ va_start (args, format);
+ vsnprintf (message, sizeof (message), optional_string (format), args);
+ va_end (args);
+
+ write_gref_message (message, gref_log);
}
[[gnu::always_inline, gnu::flatten]]
-void OSBridge::log_it (LogCategories category, std::string const& line, FILE *to, const char *const from, bool logcat_enabled) noexcept
+void OSBridge::log_it (LogCategories category, const char *line, FILE *to, const char *const from, bool logcat_enabled) noexcept
{
log_write (category, LogLevel::Info, line);
@@ -146,13 +205,28 @@ void OSBridge::log_it (LogCategories category, std::string const& line, FILE *to
return;
}
- fwrite (line.c_str (), sizeof (std::string::value_type), line.length (), to);
+ fwrite (line, sizeof (char), strlen (line), to);
fputc ('\n', to);
_write_stack_trace (to, from, category);
fflush (to);
}
+void OSBridge::log_itf (LogCategories category, FILE *to, const char *from, bool logcat_enabled, const char *format, ...) noexcept
+{
+ if (!logcat_enabled && to == nullptr) {
+ return;
+ }
+
+ char log_line[LOG_LINE_BUFFER_SIZE];
+ va_list args;
+ va_start (args, format);
+ vsnprintf (log_line, sizeof (log_line), optional_string (format), args);
+ va_end (args);
+
+ log_it (category, log_line, to, from, logcat_enabled);
+}
+
auto OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from) noexcept -> int
{
int c = _monodroid_gref_inc ();
@@ -161,8 +235,12 @@ auto OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject
}
int wc = __atomic_load_n (&gc_weak_gref_count, __ATOMIC_RELAXED);
- const std::string log_line = std::format (
- "+g+ grefc {} gwrefc {} obj-handle {:p}/{} -> new-handle {:p}/{} from thread '{}'({})"sv,
+ log_itf (
+ LOG_GREF,
+ Logger::gref_log (),
+ from,
+ Logger::gref_to_logcat (),
+ "+g+ grefc %d gwrefc %d obj-handle %p/%c -> new-handle %p/%c from thread '%s'(%d)",
c,
wc,
reinterpret_cast(curHandle),
@@ -173,7 +251,6 @@ auto OSBridge::_monodroid_gref_log_new (jobject curHandle, char curType, jobject
threadId
);
- log_it (LOG_GREF, log_line, Logger::gref_log (), from, Logger::gref_to_logcat ());
return c;
}
@@ -185,8 +262,12 @@ void OSBridge::_monodroid_gref_log_delete (jobject handle, char type, const char
}
int wc = __atomic_load_n (&gc_weak_gref_count, __ATOMIC_RELAXED);
- const std::string log_line = std::format (
- "-g- grefc {} gwrefc {} handle {:p}/{} from thread '{}'({})"sv,
+ log_itf (
+ LOG_GREF,
+ Logger::gref_log (),
+ from,
+ Logger::gref_to_logcat (),
+ "-g- grefc %d gwrefc %d handle %p/%c from thread '%s'(%d)",
c,
wc,
reinterpret_cast(handle),
@@ -194,8 +275,6 @@ void OSBridge::_monodroid_gref_log_delete (jobject handle, char type, const char
optional_string (threadName),
threadId
);
-
- log_it (LOG_GREF, log_line, Logger::gref_log (), from, Logger::gref_to_logcat ());
}
void OSBridge::_monodroid_weak_gref_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from)
@@ -206,8 +285,12 @@ void OSBridge::_monodroid_weak_gref_new (jobject curHandle, char curType, jobjec
}
int gc = __atomic_load_n (&gc_gref_count, __ATOMIC_RELAXED);
- const std::string log_line = std::format (
- "+w+ grefc {} gwrefc {} obj-handle {:p}/{} -> new-handle {:p}/{} from thread '{}'({})"sv,
+ log_itf (
+ LOG_GREF,
+ Logger::gref_log (),
+ from,
+ Logger::gref_to_logcat (),
+ "+w+ grefc %d gwrefc %d obj-handle %p/%c -> new-handle %p/%c from thread '%s'(%d)",
gc,
c,
reinterpret_cast(curHandle),
@@ -217,8 +300,6 @@ void OSBridge::_monodroid_weak_gref_new (jobject curHandle, char curType, jobjec
optional_string (threadName),
threadId
);
-
- log_it (LOG_GREF, log_line, Logger::gref_log (), from, Logger::gref_to_logcat ());
}
void
@@ -228,16 +309,18 @@ OSBridge::_monodroid_lref_log_new (int lrefc, jobject handle, char type, const c
return;
}
- const std::string log_line = std::format (
- "+l+ lrefc {} handle {:p}/{} from thread '{}'({})"sv,
+ log_itf (
+ LOG_LREF,
+ Logger::lref_log (),
+ from,
+ Logger::lref_to_logcat (),
+ "+l+ lrefc %d handle %p/%c from thread '%s'(%d)",
lrefc,
reinterpret_cast(handle),
type,
optional_string (threadName),
threadId
);
-
- log_it (LOG_LREF, log_line, Logger::lref_log (), from, Logger::lref_to_logcat ());
}
void OSBridge::_monodroid_weak_gref_delete (jobject handle, char type, const char *threadName, int threadId, const char *from)
@@ -248,8 +331,12 @@ void OSBridge::_monodroid_weak_gref_delete (jobject handle, char type, const cha
}
int gc = __atomic_load_n (&gc_gref_count, __ATOMIC_RELAXED);
- const std::string log_line = std::format (
- "-w- grefc {} gwrefc {} handle {:p}/{} from thread '{}'({})"sv,
+ log_itf (
+ LOG_GREF,
+ Logger::gref_log (),
+ from,
+ Logger::gref_to_logcat (),
+ "-w- grefc %d gwrefc %d handle %p/%c from thread '%s'(%d)",
gc,
c,
reinterpret_cast(handle),
@@ -257,8 +344,6 @@ void OSBridge::_monodroid_weak_gref_delete (jobject handle, char type, const cha
optional_string (threadName),
threadId
);
-
- log_it (LOG_GREF, log_line, Logger::gref_log (), from, Logger::gref_to_logcat ());
}
void OSBridge::_monodroid_lref_log_delete (int lrefc, jobject handle, char type, const char *threadName, int threadId, const char *from)
@@ -267,14 +352,16 @@ void OSBridge::_monodroid_lref_log_delete (int lrefc, jobject handle, char type,
return;
}
- const std::string log_line = std::format (
- "-l- lrefc {} handle {:p}/{} from thread '{}'({})"sv,
+ log_itf (
+ LOG_LREF,
+ Logger::lref_log (),
+ from,
+ Logger::lref_to_logcat (),
+ "-l- lrefc %d handle %p/%c from thread '%s'(%d)",
lrefc,
reinterpret_cast(handle),
type,
optional_string (threadName),
threadId
);
-
- log_it (LOG_LREF, log_line, Logger::lref_log (), from, Logger::lref_to_logcat ());
}
diff --git a/src/native/clr/host/typemap.cc b/src/native/clr/host/typemap.cc
index 85e5a37a3a5..3bfd47b58db 100644
--- a/src/native/clr/host/typemap.cc
+++ b/src/native/clr/host/typemap.cc
@@ -67,7 +67,7 @@ namespace {
[[gnu::always_inline, gnu::flatten]]
auto TypeMapper::find_index_by_name (const char *typeName, const TypeMapEntry *map, const char (&name_map)[], std::string_view const& from_name, std::string_view const& to_name) noexcept -> ssize_t
{
- log_debug (LOG_ASSEMBLY, "typemap: map {} -> {} uses strings", from_name, to_name);
+ log_debug (LOG_ASSEMBLY, "typemap: map %.*s -> %.*s uses strings", static_cast(from_name.length ()), from_name.data (), static_cast(to_name.length ()), to_name.data ());
auto equal = [](TypeMapEntry const& entry, const char *key, const char (&name_map)[]) -> bool {
if (entry.from == std::numeric_limits::max ()) [[unlikely]] {
@@ -97,7 +97,7 @@ auto TypeMapper::find_index_by_hash (const char *typeName, const TypeMapEntry *m
return find_index_by_name (typeName, map, name_map, from_name, to_name);
}
- log_debug (LOG_ASSEMBLY, "typemap: map {} -> {} uses hashes"sv, from_name, to_name);
+ log_debug (LOG_ASSEMBLY, "typemap: map %.*s -> %.*s uses hashes", static_cast(from_name.length ()), from_name.data (), static_cast(to_name.length ()), to_name.data ());
auto equal = [](TypeMapEntry const& entry, hash_t key) -> bool {
if (entry.from == std::numeric_limits::max ()) [[unlikely]] {
@@ -122,7 +122,7 @@ auto TypeMapper::find_index_by_hash (const char *typeName, const TypeMapEntry *m
auto TypeMapper::index_to_name (ssize_t idx, const char* typeName, const TypeMapEntry *map, const char (&name_map)[], std::string_view const& from_name, std::string_view const& to_name) -> const char*
{
if (idx < 0) [[unlikely]] {
- log_debug (LOG_ASSEMBLY, "typemap: unable to map from {} type '{}' to {} type"sv, from_name, typeName, to_name);
+ log_debug (LOG_ASSEMBLY, "typemap: unable to map from %.*s type '%s' to %.*s type", static_cast(from_name.length ()), from_name.data (), optional_string (typeName), static_cast(to_name.length ()), to_name.data ());
return nullptr;
}
@@ -131,10 +131,12 @@ auto TypeMapper::index_to_name (ssize_t idx, const char* typeName, const TypeMap
log_debug (
LOG_ASSEMBLY,
- "typemap: {} type '{}' maps to {} type '{}'"sv,
- from_name,
+ "typemap: %.*s type '%s' maps to %.*s type '%s'",
+ static_cast(from_name.length ()),
+ from_name.data (),
optional_string (typeName),
- to_name,
+ static_cast(to_name.length ()),
+ to_name.data (),
optional_string (mapped_name)
);
return mapped_name;
@@ -159,7 +161,7 @@ auto TypeMapper::managed_to_java_debug (const char *typeName, const uint8_t *mvi
// We explicitly trust the build process here, with regards to validity of offsets
full_type_name.append (&type_map_assembly_names[assm.name_offset], assm.name_length);
} else {
- log_warn (LOG_ASSEMBLY, "typemap: unable to look up assembly name for type '{}', trying without it."sv, typeName);
+ log_warn (LOG_ASSEMBLY, "typemap: unable to look up assembly name for type '%s', trying without it.", optional_string (typeName));
}
// If hashes are used for matching, the type names array is not used. If, however, string-based matching is in
@@ -220,12 +222,12 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m
if (mvid == nullptr) {
log_warn (LOG_ASSEMBLY, "typemap: no mvid specified in call to typemap_managed_to_java"sv);
} else {
- log_info (LOG_ASSEMBLY, "typemap: module matching MVID [{}] not found."sv, MonoGuidString (mvid).c_str ());
+ log_info (LOG_ASSEMBLY, "typemap: module matching MVID [%s] not found.", MonoGuidString (mvid).c_str ());
}
return nullptr;
}
- log_debug (LOG_ASSEMBLY, "typemap: found module matching MVID [{}]"sv, MonoGuidString (mvid).c_str ());
+ log_debug (LOG_ASSEMBLY, "typemap: found module matching MVID [%s]", MonoGuidString (mvid).c_str ());
hash_t name_hash = xxhash::hash (typeName, strlen (typeName));
// We implicitly trust the build process that the indexes are correct. This is by design, the libxamarin-app.so built
@@ -236,10 +238,10 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m
if (match->duplicate_count > 0 && match->duplicate_map_index < std::numeric_limitsduplicate_map_index)>::max ()) {
log_debug (
LOG_ASSEMBLY,
- "typemap: searching module [{}] duplicate map for type '{}' (hash {:x})"sv,
+ "typemap: searching module [%s] duplicate map for type '%s' (hash %zx)",
MonoGuidString (mvid).c_str (),
optional_string (typeName),
- name_hash
+ static_cast(name_hash)
);
const TypeMapModuleEntry *const duplicate_map = &modules_duplicates_data[match->duplicate_map_index];
@@ -249,11 +251,12 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m
if (entry == nullptr) {
log_warn (
LOG_ASSEMBLY,
- "typemap: managed type '{}' (hash {:x}) not found in module [{}] ({})."sv,
+ "typemap: managed type '%s' (hash %zx) not found in module [%s] (%.*s).",
optional_string (typeName),
- name_hash,
+ static_cast(name_hash),
MonoGuidString (mvid).c_str (),
- std::string_view (&managed_assembly_names[match->assembly_name_index], match->assembly_name_length)
+ static_cast(match->assembly_name_length),
+ &managed_assembly_names[match->assembly_name_index]
);
return nullptr;
}
@@ -262,11 +265,12 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m
if (entry->java_map_index >= java_type_count) [[unlikely]] {
log_warn (
LOG_ASSEMBLY,
- "typemap: managed type '{}' (hash {:x}) in module [{}] ({}) has invalid Java type index {}"sv,
+ "typemap: managed type '%s' (hash %zx) in module [%s] (%.*s) has invalid Java type index %u",
optional_string (typeName),
- name_hash,
+ static_cast(name_hash),
MonoGuidString (mvid).c_str (),
- std::string_view (&managed_assembly_names[match->assembly_name_index], match->assembly_name_length),
+ static_cast(match->assembly_name_length),
+ &managed_assembly_names[match->assembly_name_index],
entry->java_map_index
);
return nullptr;
@@ -276,11 +280,12 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m
if (java_entry.java_name_index >= java_type_names_size) [[unlikely]] {
log_warn (
LOG_ASSEMBLY,
- "typemap: managed type '{}' (hash {:x}) in module [{}] ({}) points to invalid Java type at index {} (invalid type name index {})"sv,
+ "typemap: managed type '%s' (hash %zx) in module [%s] (%.*s) points to invalid Java type at index %u (invalid type name index %u)",
optional_string (typeName),
- name_hash,
+ static_cast(name_hash),
MonoGuidString (mvid).c_str (),
- std::string_view (&managed_assembly_names[match->assembly_name_index], match->assembly_name_length),
+ static_cast(match->assembly_name_length),
+ &managed_assembly_names[match->assembly_name_index],
entry->java_map_index,
java_entry.java_name_index
);
@@ -290,17 +295,18 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m
const char *ret = &java_type_names[java_entry.java_name_index];
if (ret == nullptr) [[unlikely]] {
- log_warn (LOG_ASSEMBLY, "typemap: empty Java type name returned for entry at index {}"sv, entry->java_map_index);
+ log_warn (LOG_ASSEMBLY, "typemap: empty Java type name returned for entry at index %u", entry->java_map_index);
}
log_debug (
LOG_ASSEMBLY,
- "typemap: managed type '{}' (hash {:x}) in module [{}] ({}) corresponds to Java type '{}'"sv,
+ "typemap: managed type '%s' (hash %zx) in module [%s] (%.*s) corresponds to Java type '%s'",
optional_string (typeName),
- name_hash,
+ static_cast(name_hash),
MonoGuidString (mvid).c_str (),
- std::string_view (&managed_assembly_names[match->assembly_name_index], match->assembly_name_length),
- ret
+ static_cast(match->assembly_name_length),
+ &managed_assembly_names[match->assembly_name_index],
+ optional_string (ret)
);
return ret;
@@ -310,7 +316,7 @@ auto TypeMapper::managed_to_java_release (const char *typeName, const uint8_t *m
[[gnu::flatten]]
auto TypeMapper::managed_to_java (const char *typeName, const uint8_t *mvid) noexcept -> const char*
{
- log_debug (LOG_ASSEMBLY, "managed_to_java: looking up type '{}'"sv, optional_string (typeName));
+ log_debug (LOG_ASSEMBLY, "managed_to_java: looking up type '%s'", optional_string (typeName));
if (FastTiming::enabled ()) [[unlikely]] {
internal_timing.start_event (TimingEventKind::ManagedToJava);
}
@@ -363,10 +369,10 @@ auto TypeMapper::java_to_managed_debug (const char *java_type_name, char const**
log_debug (
LOG_ASSEMBLY,
- "Mapped Java type '{}' to managed type '{}' in assembly '{}' and with token '{:x}'"sv,
+ "Mapped Java type '%s' to managed type '%s' in assembly '%s' and with token '%x'",
optional_string (java_type_name),
- name,
- *assembly_name,
+ optional_string (name),
+ optional_string (*assembly_name),
*managed_type_token_id
);
@@ -392,8 +398,8 @@ auto TypeMapper::java_to_managed_release (const char *java_type_name, char const
if (java_type_name == nullptr) {
log_warn (
LOG_ASSEMBLY,
- "typemap: required parameter `{}` not passed to {}"sv,
- "java_type_name"sv,
+ "typemap: required parameter `%s` not passed to %s",
+ "java_type_name",
__PRETTY_FUNCTION__
);
}
@@ -401,8 +407,8 @@ auto TypeMapper::java_to_managed_release (const char *java_type_name, char const
if (assembly_name == nullptr) {
log_warn (
LOG_ASSEMBLY,
- "typemap: required parameter `{}` not passed to {}"sv,
- "assembly_name"sv,
+ "typemap: required parameter `%s` not passed to %s",
+ "assembly_name",
__PRETTY_FUNCTION__
);
}
@@ -410,8 +416,8 @@ auto TypeMapper::java_to_managed_release (const char *java_type_name, char const
if (managed_type_token_id == nullptr) {
log_warn (
LOG_ASSEMBLY,
- "typemap: required parameter `{}` not passed to {}"sv,
- "managed_type_token_id"sv,
+ "typemap: required parameter `%s` not passed to %s",
+ "managed_type_token_id",
__PRETTY_FUNCTION__
);
}
@@ -424,9 +430,9 @@ auto TypeMapper::java_to_managed_release (const char *java_type_name, char const
if (java_entry == nullptr) {
log_info (
LOG_ASSEMBLY,
- "typemap: unable to find mapping to a managed type from Java type '{}' (hash {:x})"sv,
+ "typemap: unable to find mapping to a managed type from Java type '%s' (hash %zx)",
optional_string (java_type_name),
- name_hash
+ static_cast(name_hash)
);
return false;
@@ -438,11 +444,13 @@ auto TypeMapper::java_to_managed_release (const char *java_type_name, char const
log_debug (
LOG_ASSEMBLY,
- "Java type '{}' corresponds to managed type '{}' (token 0x{:x} in assembly '{}')"sv,
+ "Java type '%s' corresponds to managed type '%.*s' (token 0x%x in assembly '%.*s')",
optional_string (java_type_name),
- std::string_view (&managed_type_names[java_entry->managed_type_name_index], java_entry->managed_type_name_length),
+ static_cast(java_entry->managed_type_name_length),
+ &managed_type_names[java_entry->managed_type_name_index],
*managed_type_token_id,
- std::string_view (&managed_assembly_names[module.assembly_name_index], module.assembly_name_length)
+ static_cast(module.assembly_name_length),
+ &managed_assembly_names[module.assembly_name_index]
);
return true;
@@ -452,7 +460,7 @@ auto TypeMapper::java_to_managed_release (const char *java_type_name, char const
[[gnu::flatten]]
auto TypeMapper::java_to_managed (const char *java_type_name, char const** assembly_name, uint32_t *managed_type_token_id) noexcept -> bool
{
- log_debug (LOG_ASSEMBLY, "java_to_managed: looking up type '{}'"sv, optional_string (java_type_name));
+ log_debug (LOG_ASSEMBLY, "java_to_managed: looking up type '%s'", optional_string (java_type_name));
if (FastTiming::enabled ()) [[unlikely]] {
internal_timing.start_event (TimingEventKind::JavaToManaged);
}
diff --git a/src/native/clr/include/host/bridge-processing-shared.hh b/src/native/clr/include/host/bridge-processing-shared.hh
index 0e0a9a6244c..35863ca4e26 100644
--- a/src/native/clr/include/host/bridge-processing-shared.hh
+++ b/src/native/clr/include/host/bridge-processing-shared.hh
@@ -1,7 +1,8 @@
#pragma once
+#include
+
#include
-#include
#include
#include
@@ -20,27 +21,65 @@ struct CrossReferenceTarget
void mark_refs_added_if_needed () noexcept;
};
+struct BridgeProcessingCallbacks
+{
+ bool (*maybe_call_gc_user_peerable_add_managed_reference) (JNIEnv *env, jobject from, jobject to) noexcept;
+ bool (*maybe_call_gc_user_peerable_clear_managed_references) (JNIEnv *env, jobject handle) noexcept;
+};
+
+class TemporaryPeerMap
+{
+public:
+ explicit TemporaryPeerMap (JNIEnv *env, MarkCrossReferencesArgs *cross_refs) noexcept;
+ ~TemporaryPeerMap () noexcept;
+
+ static void initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) noexcept;
+
+ void add (StronglyConnectedComponent &scc) noexcept;
+ bool has_temporary_peer (const StronglyConnectedComponent &scc) const noexcept;
+ jobject get (const StronglyConnectedComponent &scc) const noexcept;
+
+private:
+ // Count is unsigned, so encode the temporary peer index as ~index. This stores the same bit
+ // pattern as -(index + 1), giving us a sign bit marker while preserving index 0.
+ // The .NET 11 GC bridge implementation appears safe to temporarily mutate here: once it
+ // hands us MarkCrossReferencesArgs, it does not inspect Count again before freeing the data.
+ // The destructor always resets temporary markers before returning cross_refs to the runtime.
+ static constexpr size_t temporary_peer_index_sign_bit = ~(~size_t { 0 } >> 1);
+
+ static bool is_temporary_peer_index (size_t count) noexcept;
+ static size_t encode_temporary_peer_index (size_t index) noexcept;
+ static size_t decode_temporary_peer_index (size_t count) noexcept;
+
+ static inline jclass peer_class = nullptr;
+ static inline jmethodID peer_ctor = nullptr;
+
+ JNIEnv *env;
+ MarkCrossReferencesArgs *cross_refs;
+ jobject *peers {};
+ size_t count {};
+ size_t capacity {};
+};
+
class BridgeProcessingShared
{
public:
- explicit BridgeProcessingShared (MarkCrossReferencesArgs *args) noexcept;
- static void initialize_on_runtime_init (JNIEnv *jniEnv, jclass runtimeClass) noexcept;
+ explicit BridgeProcessingShared (MarkCrossReferencesArgs *args, const BridgeProcessingCallbacks *callbacks = nullptr) noexcept;
+
void process () noexcept;
private:
JNIEnv* env;
MarkCrossReferencesArgs *cross_refs;
- std::unordered_map temporary_peers;
-
- static inline jclass GCUserPeer_class = nullptr;
- static inline jmethodID GCUserPeer_ctor = nullptr;
+ BridgeProcessingCallbacks callbacks;
void prepare_for_java_collection () noexcept;
- void prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc) noexcept;
+ void prepare_sccs_and_cross_references_for_java_collection () noexcept;
+ void prepare_scc_for_java_collection (size_t scc_index, const StronglyConnectedComponent &scc, TemporaryPeerMap &temporary_peers) noexcept;
void take_weak_global_ref (const HandleContext &context) noexcept;
void add_circular_references (const StronglyConnectedComponent &scc) noexcept;
- void add_cross_reference (size_t source_index, size_t dest_index) noexcept;
- CrossReferenceTarget select_cross_reference_target (size_t scc_index) noexcept;
+ void add_cross_reference (size_t source_index, size_t dest_index, TemporaryPeerMap &temporary_peers) noexcept;
+ CrossReferenceTarget select_cross_reference_target (size_t scc_index, TemporaryPeerMap &temporary_peers) noexcept;
bool add_reference (jobject from, jobject to) noexcept;
void cleanup_after_java_collection () noexcept;
@@ -50,6 +89,8 @@ private:
void clear_references_if_needed (const HandleContext &context) noexcept;
void clear_references (jobject handle) noexcept;
+ bool maybe_call_gc_user_peerable_add_managed_reference (JNIEnv *env, jobject from, jobject to) noexcept;
+ bool maybe_call_gc_user_peerable_clear_managed_references (JNIEnv *env, jobject handle) noexcept;
void log_missing_add_references_method (jclass java_class) noexcept;
void log_missing_clear_references_method (jclass java_class) noexcept;
@@ -60,9 +101,4 @@ private:
void log_gref_delete (jobject handle) noexcept;
void log_weak_ref_delete (jobject weak) noexcept;
void log_gc_summary () noexcept;
-
- // These methods must be implemented by every host individually
- // Both methods below return `true` if they processed the call
- virtual auto maybe_call_gc_user_peerable_add_managed_reference (JNIEnv *env, jobject from, jobject to) noexcept -> bool = 0;
- virtual auto maybe_call_gc_user_peerable_clear_managed_references (JNIEnv *env, jobject handle) noexcept -> bool = 0;
};
diff --git a/src/native/clr/include/host/bridge-processing.hh b/src/native/clr/include/host/bridge-processing.hh
index 2d638c0e6ba..1a2ab64dd1d 100644
--- a/src/native/clr/include/host/bridge-processing.hh
+++ b/src/native/clr/include/host/bridge-processing.hh
@@ -10,15 +10,4 @@ public:
explicit BridgeProcessing (MarkCrossReferencesArgs *args) noexcept
: BridgeProcessingShared (args)
{}
-
-private:
- auto maybe_call_gc_user_peerable_add_managed_reference ([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject from, [[maybe_unused]] jobject to) noexcept -> bool override final
- {
- return false; // no-op for CoreCLR, we didn't process the call
- }
-
- auto maybe_call_gc_user_peerable_clear_managed_references ([[maybe_unused]] JNIEnv *env, [[maybe_unused]] jobject handle) noexcept -> bool override final
- {
- return false; // no-op for CoreCLR, we didn't process the call
- }
};
diff --git a/src/native/clr/include/host/gc-bridge.hh b/src/native/clr/include/host/gc-bridge.hh
index 6430e6f237a..ce1e7844b59 100644
--- a/src/native/clr/include/host/gc-bridge.hh
+++ b/src/native/clr/include/host/gc-bridge.hh
@@ -1,11 +1,9 @@
#pragma once
-#include
+#include
+#include
+
#include
-#include
-#include
-#include
-#include
#include
@@ -74,8 +72,14 @@ namespace xamarin::android {
GCBridge::bridge_processing_started_callback = bridge_processing_started;
GCBridge::bridge_processing_finished_callback = bridge_processing_finished;
- bridge_processing_thread = std::thread { GCBridge::bridge_processing };
- bridge_processing_thread.detach ();
+ int ret = sem_init (&shared_args_semaphore, 0, 0);
+ abort_unless (ret == 0, "Failed to initialize GC bridge semaphore");
+
+ ret = pthread_create (&bridge_processing_thread, nullptr, GCBridge::bridge_processing_thread_entry, nullptr);
+ abort_unless (ret == 0, "Failed to create GC bridge processing thread");
+
+ ret = pthread_detach (bridge_processing_thread);
+ abort_unless (ret == 0, "Failed to detach GC bridge processing thread");
return mark_cross_references;
}
@@ -83,10 +87,9 @@ namespace xamarin::android {
static void trigger_java_gc (JNIEnv *env) noexcept;
private:
- static inline std::thread bridge_processing_thread {};
-
- static inline std::binary_semaphore shared_args_semaphore{0};
- static inline std::atomic shared_args;
+ static inline pthread_t bridge_processing_thread {};
+ static inline sem_t shared_args_semaphore {};
+ static inline MarkCrossReferencesArgs *shared_args = nullptr;
static inline jobject Runtime_instance = nullptr;
static inline jmethodID Runtime_gc = nullptr;
@@ -96,7 +99,11 @@ namespace xamarin::android {
static inline BridgeProcessingFinishedFtn bridge_processing_finished_callback = nullptr;
static void bridge_processing () noexcept;
+ static auto bridge_processing_thread_entry (void *arg) noexcept -> void*;
static void mark_cross_references (MarkCrossReferencesArgs *args) noexcept;
+ static MarkCrossReferencesArgs* enter_bridge_processing () noexcept;
+ static void send_and_signal (MarkCrossReferencesArgs *args) noexcept;
+ static void process_bridge_args (MarkCrossReferencesArgs *args) noexcept;
static void log_mark_cross_references_args_if_enabled (MarkCrossReferencesArgs *args) noexcept;
static void log_handle_context (JNIEnv *env, HandleContext *ctx) noexcept;
diff --git a/src/native/clr/include/host/host-environment.hh b/src/native/clr/include/host/host-environment.hh
index 2c4dc95252d..7ef13ba5835 100644
--- a/src/native/clr/include/host/host-environment.hh
+++ b/src/native/clr/include/host/host-environment.hh
@@ -42,7 +42,9 @@ namespace xamarin::android {
static void set_system_property (const char *name, const char *value) noexcept
{
// TODO: should we **actually** try to set the system property here? Would that even work? Needs testing
- log_debug (LOG_DEFAULT, " System property {} = '{}'", optional_string (name), optional_string (value));
+ if ((log_categories & LOG_DEFAULT) != 0) {
+ log_write_fmt (LOG_DEFAULT, LogLevel::Debug, " System property %s = '%s'", optional_string (name), optional_string (value));
+ }
}
[[gnu::flatten, gnu::always_inline]]
@@ -88,10 +90,12 @@ namespace xamarin::android {
static_local_string dir (home_len + relative_path.length ());
Util::path_combine (dir, home.get_string_view (), relative_path);
- log_debug (LOG_DEFAULT, "Creating XDG directory: {}"sv, optional_string (dir.get ()));
+ if ((log_categories & LOG_DEFAULT) != 0) {
+ log_write_fmt (LOG_DEFAULT, LogLevel::Debug, "Creating XDG directory: %s", optional_string (dir.get ()));
+ }
int rv = Util::create_directory (dir.get (), Constants::DEFAULT_DIRECTORY_MODE);
if (rv < 0 && errno != EEXIST) {
- log_warn (LOG_DEFAULT, "Failed to create XDG directory {}. {}"sv, optional_string (dir.get ()), strerror (errno));
+ log_write_fmt (LOG_DEFAULT, LogLevel::Warn, "Failed to create XDG directory %s. %s", optional_string (dir.get ()), strerror (errno));
}
if (!environment_variable_name.empty ()) {
diff --git a/src/native/clr/include/host/os-bridge.hh b/src/native/clr/include/host/os-bridge.hh
index ee96c220671..a7324f52d47 100644
--- a/src/native/clr/include/host/os-bridge.hh
+++ b/src/native/clr/include/host/os-bridge.hh
@@ -31,6 +31,13 @@ namespace xamarin::android {
static auto _monodroid_weak_gref_dec () noexcept -> int;
static void _monodroid_gref_log (const char *message) noexcept;
+
+ template
+ static void _monodroid_gref_log (const char *format, Arg arg, Args... args) noexcept
+ {
+ _monodroid_gref_log_fmt (format, arg, args...);
+ }
+
static auto _monodroid_gref_log_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from) noexcept -> int;
static void _monodroid_gref_log_delete (jobject handle, char type, const char *threadName, int threadId, const char *from) noexcept;
static void _monodroid_weak_gref_new (jobject curHandle, char curType, jobject newHandle, char newType, const char *threadName, int threadId, const char *from);
@@ -57,7 +64,9 @@ namespace xamarin::android {
private:
static void _write_stack_trace (FILE *to, const char *const from, LogCategories = LOG_NONE) noexcept;
- static void log_it (LogCategories category, std::string const& line, FILE *to, const char *const from, bool logcat_enabled) noexcept;
+ static void _monodroid_gref_log_fmt (const char *format, ...) noexcept __attribute__ ((format (printf, 1, 2)));
+ static void log_it (LogCategories category, const char *line, FILE *to, const char *const from, bool logcat_enabled) noexcept;
+ static void log_itf (LogCategories category, FILE *to, const char *from, bool logcat_enabled, const char *format, ...) noexcept __attribute__ ((format (printf, 5, 6)));
private:
static inline JavaVM *jvm = nullptr;
diff --git a/src/native/clr/include/host/pinvoke-override-impl.hh b/src/native/clr/include/host/pinvoke-override-impl.hh
index 97608211f37..5301cfbe470 100644
--- a/src/native/clr/include/host/pinvoke-override-impl.hh
+++ b/src/native/clr/include/host/pinvoke-override-impl.hh
@@ -34,7 +34,7 @@ namespace xamarin::android {
short_library_name.append (Constants::dso_suffix);
}
- log_debug (LOG_ASSEMBLY, "Modified p/invoke library name to '{}'", short_library_name.get ());
+ log_debug (LOG_ASSEMBLY, "Modified p/invoke library name to '%s'", short_library_name.get ());
lib_handle = MonodroidDl::monodroid_dlopen (short_library_name.get (), microsoft::java_interop::JAVA_INTEROP_LIB_LOAD_LOCALLY);
}
@@ -43,21 +43,21 @@ namespace xamarin::android {
}
if (lib_handle == nullptr) {
- log_warn (LOG_ASSEMBLY, "Shared library '{}' not loaded, p/invoke '{}' may fail", library_name, symbol_name);
+ log_warn (LOG_ASSEMBLY, "Shared library '%.*s' not loaded, p/invoke '%.*s' may fail", static_cast(library_name.length ()), library_name.data (), static_cast(symbol_name.length ()), symbol_name.data ());
return nullptr;
}
if (dso_handle != nullptr) {
void *expected_null = nullptr;
if (!__atomic_compare_exchange (dso_handle, &expected_null, &lib_handle, false /* weak */, __ATOMIC_ACQUIRE /* success_memorder */, __ATOMIC_RELAXED /* xxxfailure_memorder */)) {
- log_debug (LOG_ASSEMBLY, "Library '{}' handle already cached by another thread", library_name);
+ log_debug (LOG_ASSEMBLY, "Library '%.*s' handle already cached by another thread", static_cast(library_name.length ()), library_name.data ());
}
}
}
void *entry_handle = MonodroidDl::monodroid_dlsym (lib_handle, symbol_name);
if (entry_handle == nullptr) {
- log_warn (LOG_ASSEMBLY, "Symbol '{}' not found in shared library '{}', p/invoke may fail", symbol_name, library_name);
+ log_warn (LOG_ASSEMBLY, "Symbol '%.*s' not found in shared library '%.*s', p/invoke may fail", static_cast(symbol_name.length ()), symbol_name.data (), static_cast(library_name.length ()), library_name.data ());
return nullptr;
}
@@ -79,7 +79,7 @@ namespace xamarin::android {
return nullptr;
}
- log_debug (LOG_ASSEMBLY, "Caching p/invoke entry {} @ {}", library_name, entrypoint_name);
+ log_debug (LOG_ASSEMBLY, "Caching p/invoke entry %s @ %s", library_name.c_str (), entrypoint_name.c_str ());
(*api_map)[entrypoint_name] = entry_handle;
return entry_handle;
}
@@ -100,7 +100,7 @@ namespace xamarin::android {
);
if (already_loaded) {
- log_debug (LOG_ASSEMBLY, "Entry '{}' from library '{}' already loaded by another thread", entrypoint_name, library_name);
+ log_debug (LOG_ASSEMBLY, "Entry '%.*s' from library '%.*s' already loaded by another thread", static_cast(entrypoint_name.length ()), entrypoint_name.data (), static_cast(library_name.length ()), library_name.data ());
}
}
@@ -165,7 +165,7 @@ namespace xamarin::android {
handle = fetch_or_create_pinvoke_map_entry (lib_name, entry_name, entrypoint_name_hash, lib_map, /* need_lock */ false);
} else {
if (iter->second == nullptr) [[unlikely]] {
- log_warn (LOG_ASSEMBLY, "Internal error: null entry in p/invoke map for key '{}'", library_name);
+ log_warn (LOG_ASSEMBLY, "Internal error: null entry in p/invoke map for key '%.*s'", static_cast(library_name.length ()), library_name.data ());
return nullptr; // fall back to `monodroid_dlopen`
}
diff --git a/src/native/clr/include/runtime-base/android-system.hh b/src/native/clr/include/runtime-base/android-system.hh
index b60871a93c4..3de8138522d 100644
--- a/src/native/clr/include/runtime-base/android-system.hh
+++ b/src/native/clr/include/runtime-base/android-system.hh
@@ -1,7 +1,9 @@
#pragma once
#include
+#include
#include
+#include
#include
#include
#include
@@ -18,6 +20,7 @@ struct BundledProperty;
namespace xamarin::android {
class AndroidSystem
{
+#if !defined(XA_HOST_NATIVEAOT)
// This optimizes things a little bit. The array is allocated at build time, so we pay no cost for its
// allocation and at run time it allows us to skip dynamic memory allocation.
inline static std::array single_app_lib_directory{};
@@ -35,6 +38,7 @@ namespace xamarin::android {
std::string_view { "x86_64" }, // CPU_KIND_X86_64
std::string_view { "riscv" }, // CPU_KIND_RISCV
};
+#endif
public:
static auto get_gref_gc_threshold () noexcept -> long
@@ -60,22 +64,23 @@ namespace xamarin::android {
running_in_emulator = yesno;
}
- static auto get_primary_override_dir () noexcept -> std::string const&
+ static auto get_primary_override_dir () noexcept -> const char*
{
return primary_override_dir;
}
static void set_primary_override_dir (jstring_wrapper& home) noexcept
{
- primary_override_dir = determine_primary_override_dir (home);
+ determine_primary_override_dir (home, primary_override_dir, sizeof (primary_override_dir));
}
+#if !defined(XA_HOST_NATIVEAOT)
static auto get_native_libraries_dir () noexcept -> std::string const&
{
return native_libraries_dir;
}
- static void create_update_dir (std::string const& override_dir) noexcept
+ static void create_update_dir (const char *override_dir) noexcept
{
if constexpr (Constants::is_release_build) {
/*
@@ -91,9 +96,10 @@ namespace xamarin::android {
}
}
- log_debug (LOG_DEFAULT, "Creating public update directory: `{}`", override_dir);
+ log_debug (LOG_DEFAULT, "Creating public update directory: `%s`", optional_string (override_dir));
Util::create_public_directory (override_dir);
}
+#endif
static auto is_embedded_dso_mode_enabled () noexcept -> bool
{
@@ -129,7 +135,7 @@ namespace xamarin::android {
embedded_dso_mode_enabled = yesno;
}
- static auto determine_primary_override_dir (jstring_wrapper &home) noexcept -> std::string
+ static void determine_primary_override_dir (jstring_wrapper &home, char *buffer, size_t buffer_size) noexcept
{
dynamic_local_string name { home.get_cstr () };
name.append ("/")
@@ -137,18 +143,20 @@ namespace xamarin::android {
.append ("/")
.append (Constants::android_lib_abi);
- return {name.get (), name.length ()};
+ snprintf (buffer, buffer_size, "%s", name.get ());
}
private:
static inline long max_gref_count = 0;
static inline bool running_in_emulator = false;
static inline bool embedded_dso_mode_enabled = false;
- static inline std::string primary_override_dir;
+ static inline char primary_override_dir[SENSIBLE_PATH_MAX] {};
+#if !defined(XA_HOST_NATIVEAOT)
static inline std::string native_libraries_dir;
#if defined (DEBUG)
static inline std::unordered_map bundled_properties;
+#endif
#endif
};
}
diff --git a/src/native/clr/include/runtime-base/monodroid-dl.hh b/src/native/clr/include/runtime-base/monodroid-dl.hh
index cc2973b4515..5f39aed3d24 100644
--- a/src/native/clr/include/runtime-base/monodroid-dl.hh
+++ b/src/native/clr/include/runtime-base/monodroid-dl.hh
@@ -41,11 +41,11 @@ namespace xamarin::android
size_t arr_size;
if constexpr (WhichCache == CacheKind::AOT) {
- log_debug (LOG_ASSEMBLY, "Looking for hash {:x} in AOT cache", hash);
+ log_debug (LOG_ASSEMBLY, "Looking for hash %zx in AOT cache", static_cast(hash));
arr = aot_dso_cache;
arr_size = application_config.number_of_aot_cache_entries;
} else if constexpr (WhichCache == CacheKind::DSO) {
- log_debug (LOG_ASSEMBLY, "Looking for hash {:x} in DSO cache", hash);
+ log_debug (LOG_ASSEMBLY, "Looking for hash %zx in DSO cache", static_cast(hash));
arr = dso_cache;
arr_size = application_config.number_of_dso_cache_entries;
}
@@ -104,23 +104,24 @@ namespace xamarin::android
[[gnu::flatten]]
static auto monodroid_dlopen (DSOCacheEntry *dso, std::string_view const& name, int flags) noexcept -> void*
{
- log_debug (LOG_ASSEMBLY, "monodroid_dlopen: hash match {}found, DSO name is '{}'", dso == nullptr ? "not "sv : ""sv, get_dso_name (dso));
+ std::string_view dso_name = get_dso_name (dso);
+ std::string_view match_status = dso == nullptr ? "not "sv : ""sv;
+ log_debug (LOG_ASSEMBLY, "monodroid_dlopen: hash match %.*sfound, DSO name is '%.*s'", static_cast(match_status.length ()), match_status.data (), static_cast(dso_name.length ()), dso_name.data ());
if (dso == nullptr) {
// DSO not known at build time, try to load it. Since we don't know whether or not the library uses
// JNI, we're going to assume it does and thus use System.loadLibrary eventually.
return DsoLoader::load (name, flags, true /* is_jni */);
} else if (dso->handle != nullptr) {
- log_debug (LOG_ASSEMBLY, "monodroid_dlopen: library {} already loaded, returning handle {:p}", name, dso->handle);
+ log_debug (LOG_ASSEMBLY, "monodroid_dlopen: library %.*s already loaded, returning handle %p", static_cast(name.length ()), name.data (), dso->handle);
return dso->handle;
}
if (dso->ignore) {
- log_info (LOG_ASSEMBLY, "Request to load '{}' ignored, it is known not to exist", get_dso_name (dso));
+ log_info (LOG_ASSEMBLY, "Request to load '%.*s' ignored, it is known not to exist", static_cast(dso_name.length ()), dso_name.data ());
return nullptr;
}
- std::string_view dso_name = get_dso_name (dso);
StartupAwareLock lock (dso_handle_write_lock);
#if defined (RELEASE)
if (AndroidSystem::is_embedded_dso_mode_enabled ()) {
@@ -153,7 +154,7 @@ namespace xamarin::android
}
hash_t name_hash = xxhash::hash (name.data (), name.size ());
- log_debug (LOG_ASSEMBLY, "monodroid_dlopen: hash for name '{}' is {:x}", name, name_hash);
+ log_debug (LOG_ASSEMBLY, "monodroid_dlopen: hash for name '%.*s' is %zx", static_cast(name.length ()), name.data (), static_cast(name_hash));
DSOCacheEntry *dso = nullptr;
if constexpr (PREFER_AOT_CACHE) {
@@ -194,8 +195,9 @@ namespace xamarin::android
if (s == nullptr) {
log_error (
LOG_ASSEMBLY,
- "Could not find symbol '{}': {}",
- name,
+ "Could not find symbol '%.*s': %s",
+ static_cast(name.length ()),
+ name.data (),
optional_string (e)
);
}
diff --git a/src/native/clr/include/runtime-base/util.hh b/src/native/clr/include/runtime-base/util.hh
index 656a3d4a68a..718b4f5d2e8 100644
--- a/src/native/clr/include/runtime-base/util.hh
+++ b/src/native/clr/include/runtime-base/util.hh
@@ -6,8 +6,10 @@
#include
#include
+#include
#include
#include
+#include
#include
#include
@@ -41,6 +43,17 @@ namespace xamarin::android {
void *area;
size_t size;
};
+
+ static constexpr auto printf_precision (std::string_view const& value) noexcept -> int
+ {
+ constexpr size_t max_precision = static_cast(std::numeric_limits::max ());
+ return value.length () > max_precision ? std::numeric_limits::max () : static_cast(value.length ());
+ }
+
+ static constexpr auto printf_string (std::string_view const& value) noexcept -> const char*
+ {
+ return value.data () == nullptr ? "" : value.data ();
+ }
}
class Util
@@ -139,7 +152,7 @@ namespace xamarin::android {
{
struct stat sbuf;
if (fstatat (dirfd, file_name, &sbuf, 0) == -1) {
- log_warn (LOG_ASSEMBLY, "Failed to stat file '{}': {}", file_name, std::strerror (errno));
+ log_write_fmt (LOG_ASSEMBLY, LogLevel::Warn, "Failed to stat file '%s': %s", file_name, std::strerror (errno));
return std::nullopt;
}
@@ -154,9 +167,11 @@ namespace xamarin::android {
[[gnu::flatten, gnu::always_inline]]
static void set_environment_variable (const char *name, const char *value) noexcept
{
- log_debug (LOG_DEFAULT, "Setting environment variable {} = '{}'", optional_string (name), optional_string (value));
+ if (should_log (LOG_DEFAULT)) {
+ log_write_fmt (LOG_DEFAULT, LogLevel::Debug, "Setting environment variable %s = '%s'", optional_string (name), optional_string (value));
+ }
if (::setenv (name, value, 1) < 0) {
- log_warn (LOG_DEFAULT, "Failed to set environment variable '{}': {}", name, ::strerror (errno));
+ log_write_fmt (LOG_DEFAULT, LogLevel::Warn, "Failed to set environment variable '%s': %s", optional_string (name), ::strerror (errno));
}
}
@@ -178,7 +193,7 @@ namespace xamarin::android {
if (createDirectory) {
int rv = create_directory (value.get_cstr (), mode);
if (rv < 0 && errno != EEXIST) {
- log_warn (LOG_DEFAULT, "Failed to create directory '{}' for environment variable '{}'. {}", value.get_string_view (), name, strerror (errno));
+ log_write_fmt (LOG_DEFAULT, LogLevel::Warn, "Failed to create directory '%s' for environment variable '%s'. %s", optional_string (value.get_cstr ()), name.data (), strerror (errno));
}
}
set_environment_variable (name, value);
@@ -208,14 +223,14 @@ namespace xamarin::android {
mmap_info.area = mmap (nullptr, offsetSize, PROT_READ, MAP_PRIVATE, fd, static_cast(offsetPage));
if (mmap_info.area == MAP_FAILED) {
- Helpers::abort_application (
+ Helpers::abort_applicationf (
LOG_ASSEMBLY,
- std::format (
- "Could not mmap APK fd {}: {}; File={}",
- fd,
- strerror (errno),
- filename
- )
+ std::source_location::current (),
+ "Could not mmap APK fd %d: %s; File=%.*s",
+ fd,
+ strerror (errno),
+ detail::printf_precision (filename),
+ detail::printf_string (filename)
);
}
@@ -223,18 +238,22 @@ namespace xamarin::android {
file_info.area = pointer_add (mmap_info.area, offsetFromPage);
file_info.size = size;
- log_info (
- LOG_ASSEMBLY,
- " mmap_start: {:<8p}; mmap_end: {:<8p} mmap_len: {:<12} file_start: {:<8p} file_end: {:<8p} file_len: {:<12} apk descriptor: {} file: {}",
- mmap_info.area,
- pointer_add (mmap_info.area, mmap_info.size),
- mmap_info.size,
- file_info.area,
- pointer_add (file_info.area, file_info.size),
- file_info.size,
- fd,
- filename
- );
+ if (should_log (LOG_ASSEMBLY)) {
+ log_write_fmt (
+ LOG_ASSEMBLY,
+ LogLevel::Info,
+ " mmap_start: %-8p; mmap_end: %-8p mmap_len: %-12zu file_start: %-8p file_end: %-8p file_len: %-12zu apk descriptor: %d file: %.*s",
+ mmap_info.area,
+ pointer_add (mmap_info.area, mmap_info.size),
+ mmap_info.size,
+ file_info.area,
+ pointer_add (file_info.area, file_info.size),
+ file_info.size,
+ fd,
+ detail::printf_precision (filename),
+ detail::printf_string (filename)
+ );
+ }
return file_info;
}
@@ -256,7 +275,9 @@ namespace xamarin::android {
elf_header->e_ident[EI_MAG1] != ELFMAG1 ||
elf_header->e_ident[EI_MAG2] != ELFMAG2 ||
elf_header->e_ident[EI_MAG3] != ELFMAG3) {
- log_debug (LOG_ASSEMBLY, "Not an ELF image: {}", file_name);
+ if (should_log (LOG_ASSEMBLY)) {
+ log_write_fmt (LOG_ASSEMBLY, LogLevel::Debug, "Not an ELF image: %.*s", detail::printf_precision (file_name), detail::printf_string (file_name));
+ }
// Not an ELF image, just return what we mmapped before
return { map_info.area, map_info.size };
}
diff --git a/src/native/clr/include/shared/log_types.hh b/src/native/clr/include/shared/log_types.hh
index 7aef6e20456..7b5d32b76e4 100644
--- a/src/native/clr/include/shared/log_types.hh
+++ b/src/native/clr/include/shared/log_types.hh
@@ -1,7 +1,7 @@
#pragma once
+#include
#include
-#include
#include
#include
@@ -20,33 +20,31 @@
#define DO_LOG_FMT(_level, _category_, _fmt_, ...) \
do { \
if ((log_categories & ((_category_))) != 0) { \
- ::log_ ## _level ## _nocheck_fmt ((_category_), _fmt_ __VA_OPT__(,) __VA_ARGS__); \
+ ::xamarin::android::log_ ## _level ## _fmt ((_category_), _fmt_ __VA_OPT__(,) __VA_ARGS__); \
} \
} while (0)
-//
-// For std::format spec, see https://en.cppreference.com/w/cpp/utility/format/spec
-//
-
-// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style
+// NOTE: _fmt_ takes arguments in POSIX printf style.
#define log_debug(_category_, _fmt_, ...) DO_LOG_FMT (debug, (_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__)
-// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style
+// NOTE: _fmt_ takes arguments in POSIX printf style.
#define log_info(_category_, _fmt_, ...) DO_LOG_FMT (info, (_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__)
-// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style
-#define log_warn(_category_, _fmt_, ...) log_warn_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__)
+// NOTE: _fmt_ takes arguments in POSIX printf style.
+#define log_warn(_category_, _fmt_, ...) ::xamarin::android::log_warn_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__)
-// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style
-#define log_error(_category_, _fmt_, ...) log_error_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__)
+// NOTE: _fmt_ takes arguments in POSIX printf style.
+#define log_error(_category_, _fmt_, ...) ::xamarin::android::log_error_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__)
-// NOTE: _fmt_ takes arguments in the std::format style not the POSIX printf style
-#define log_fatal(_category_, _fmt_, ...) log_fatal_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__)
+// NOTE: _fmt_ takes arguments in POSIX printf style.
+#define log_fatal(_category_, _fmt_, ...) ::xamarin::android::log_fatal_fmt ((_category_), (_fmt_) __VA_OPT__(,) __VA_ARGS__)
namespace xamarin::android {
// A slightly faster alternative to other log functions as it doesn't parse the message
// for format placeholders nor it uses variable arguments
void log_write (LogCategories category, LogLevel level, const char *message) noexcept;
+ void log_writev (LogCategories category, LogLevel level, const char *format, va_list args) noexcept;
+ void log_write_fmt (LogCategories category, LogLevel level, const char *format, ...) noexcept __attribute__ ((format (printf, 3, 4)));
[[gnu::always_inline]]
static inline void log_write (LogCategories category, LogLevel level, std::string_view const& message) noexcept
@@ -54,71 +52,101 @@ namespace xamarin::android {
log_write (category, level, message.data ());
}
- template [[gnu::always_inline]]
- static inline constexpr void log_write_fmt (LogCategories category, LogLevel level, std::format_string fmt, Args&& ...args)
+ void log_debug_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3)));
+ void log_info_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3)));
+ void log_warn_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3)));
+ void log_error_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3)));
+ void log_fatal_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3)));
+
+ [[gnu::always_inline]]
+ static inline void log_debug_fmt (LogCategories category, std::string_view const& message) noexcept
{
- log_write (category, level, std::format (fmt, std::forward(args)...).c_str ());
+ log_write (category, LogLevel::Debug, message);
}
-}
-template [[gnu::always_inline]]
-static inline constexpr void log_debug_nocheck_fmt (LogCategories category, std::format_string fmt, Args&& ...args)
-{
- log_write (category, xamarin::android::LogLevel::Debug, std::format (fmt, std::forward(args)...).c_str ());
-}
+ [[gnu::always_inline]]
+ static inline void log_info_fmt (LogCategories category, std::string_view const& message) noexcept
+ {
+ log_write (category, LogLevel::Info, message);
+ }
-[[gnu::always_inline]]
-static inline constexpr void log_debug_nocheck (LogCategories category, std::string_view const& message) noexcept
-{
- log_write (category, xamarin::android::LogLevel::Debug, message.data ());
-}
+ [[gnu::always_inline]]
+ static inline void log_warn_fmt (LogCategories category, std::string_view const& message) noexcept
+ {
+ log_write (category, LogLevel::Warn, message);
+ }
-template [[gnu::always_inline]]
-static inline constexpr void log_info_nocheck_fmt (LogCategories category, std::format_string fmt, Args&& ...args)
-{
- log_write (category, xamarin::android::LogLevel::Info, std::format (fmt, std::forward(args)...).c_str ());
-}
+ [[gnu::always_inline]]
+ static inline void log_error_fmt (LogCategories category, std::string_view const& message) noexcept
+ {
+ log_write (category, LogLevel::Error, message);
+ }
-[[gnu::always_inline]]
-static inline constexpr void log_info_nocheck (LogCategories category, std::string_view const& message) noexcept
-{
- log_write (category, xamarin::android::LogLevel::Info, message.data ());
-}
+ [[gnu::always_inline]]
+ static inline void log_fatal_fmt (LogCategories category, std::string_view const& message) noexcept
+ {
+ log_write (category, LogLevel::Fatal, message);
+ }
-template [[gnu::always_inline]]
-static inline constexpr void log_warn_fmt (LogCategories category, std::format_string fmt, Args&& ...args) noexcept
-{
- log_write (category, xamarin::android::LogLevel::Warn, std::format (fmt, std::forward(args)...).c_str ());
-}
+ [[gnu::always_inline]]
+ static inline void log_debug_fmt (LogCategories category, std::string const& message) noexcept
+ {
+ log_debug_fmt (category, std::string_view { message });
+ }
-[[gnu::always_inline]]
-static inline constexpr void log_warn_fmt (LogCategories category, std::string_view const& message) noexcept
-{
- log_write (category, xamarin::android::LogLevel::Warn, message.data ());
+ [[gnu::always_inline]]
+ static inline void log_info_fmt (LogCategories category, std::string const& message) noexcept
+ {
+ log_info_fmt (category, std::string_view { message });
+ }
+
+ [[gnu::always_inline]]
+ static inline void log_warn_fmt (LogCategories category, std::string const& message) noexcept
+ {
+ log_warn_fmt (category, std::string_view { message });
+ }
+
+ [[gnu::always_inline]]
+ static inline void log_error_fmt (LogCategories category, std::string const& message) noexcept
+ {
+ log_error_fmt (category, std::string_view { message });
+ }
+
+ [[gnu::always_inline]]
+ static inline void log_fatal_fmt (LogCategories category, std::string const& message) noexcept
+ {
+ log_fatal_fmt (category, std::string_view { message });
+ }
}
-template [[gnu::always_inline]]
-static inline constexpr void log_error_fmt (LogCategories category, std::format_string fmt, Args&& ...args) noexcept
+[[gnu::always_inline]]
+static inline constexpr void log_debug_nocheck (LogCategories category, std::string_view const& message) noexcept
{
- log_write (category, xamarin::android::LogLevel::Error, std::format (fmt, std::forward(args)...).c_str ());
+ xamarin::android::log_write (category, xamarin::android::LogLevel::Debug, message.data ());
}
[[gnu::always_inline]]
-static inline constexpr void log_error_fmt (LogCategories category, std::string_view const& message) noexcept
+static inline constexpr void log_info_nocheck (LogCategories category, std::string_view const& message) noexcept
{
- log_write (category, xamarin::android::LogLevel::Error, message.data ());
+ xamarin::android::log_write (category, xamarin::android::LogLevel::Info, message.data ());
}
-template [[gnu::always_inline]]
-static inline constexpr void log_fatal_fmt (LogCategories category, std::format_string fmt, Args&& ...args) noexcept
+static inline void log_debug_nocheck_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3)));
+static inline void log_debug_nocheck_fmt (LogCategories category, const char *format, ...) noexcept
{
- log_write (category, xamarin::android::LogLevel::Fatal, std::format (fmt, std::forward(args)...).c_str ());
+ va_list args;
+ va_start (args, format);
+ xamarin::android::log_writev (category, xamarin::android::LogLevel::Debug, format, args);
+ va_end (args);
}
-[[gnu::always_inline]]
-static inline constexpr void log_fatal_fmt (LogCategories category, std::string_view const& message) noexcept
+static inline void log_info_nocheck_fmt (LogCategories category, const char *format, ...) noexcept __attribute__ ((format (printf, 2, 3)));
+static inline void log_info_nocheck_fmt (LogCategories category, const char *format, ...) noexcept
{
- log_write (category, xamarin::android::LogLevel::Fatal, message.data ());
+ va_list args;
+ va_start (args, format);
+ xamarin::android::log_writev (category, xamarin::android::LogLevel::Info, format, args);
+ va_end (args);
}
extern unsigned int log_categories;
diff --git a/src/native/clr/pinvoke-override/dynamic.cc b/src/native/clr/pinvoke-override/dynamic.cc
index 5352aacab68..6633baeef24 100644
--- a/src/native/clr/pinvoke-override/dynamic.cc
+++ b/src/native/clr/pinvoke-override/dynamic.cc
@@ -24,7 +24,7 @@ extern "C" {
[[gnu::flatten]]
auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, const char *entrypoint_name) noexcept -> void*
{
- log_debug (LOG_ASSEMBLY, "library_name == '{}'; entrypoint_name == '{}'"sv, optional_string (library_name), optional_string (entrypoint_name));
+ log_debug (LOG_ASSEMBLY, "library_name == '%s'; entrypoint_name == '%s'", optional_string (library_name), optional_string (entrypoint_name));
if (library_name == nullptr || entrypoint_name == nullptr) [[unlikely]] {
Helpers::abort_application (
@@ -39,12 +39,12 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons
hash_t library_name_hash = xxhash::hash (library_name, strlen (library_name));
hash_t entrypoint_hash = xxhash::hash (entrypoint_name, strlen (entrypoint_name));
- log_debug (LOG_ASSEMBLY, "library_name_hash == 0x{:x}; entrypoint_hash == 0x{:x}"sv, library_name_hash, entrypoint_hash);
+ log_debug (LOG_ASSEMBLY, "library_name_hash == 0x%zx; entrypoint_hash == 0x%zx", static_cast(library_name_hash), static_cast(entrypoint_hash));
bool known_library = true;
void *pinvoke_ptr = find_pinvoke (library_name_hash, entrypoint_hash, known_library);
if (pinvoke_ptr != nullptr) [[likely]] {
- log_debug (LOG_ASSEMBLY, "pinvoke_ptr == {:p}"sv, pinvoke_ptr);
+ log_debug (LOG_ASSEMBLY, "pinvoke_ptr == %p", pinvoke_ptr);
return pinvoke_ptr;
}
@@ -70,7 +70,7 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons
log_debug (LOG_ASSEMBLY, "p/invoke not from a known library, slow path taken."sv);
pinvoke_ptr = handle_other_pinvoke_request (library_name, library_name_hash, entrypoint_name, entrypoint_hash);
- log_debug (LOG_ASSEMBLY, "foreign library pinvoke_ptr == {:p}"sv, pinvoke_ptr);
+ log_debug (LOG_ASSEMBLY, "foreign library pinvoke_ptr == %p", pinvoke_ptr);
return pinvoke_ptr;
}
@@ -96,8 +96,8 @@ void PinvokeOverride::handle_jni_on_load (JavaVM *vm, void *reserved) noexcept
const void* Host::clr_pinvoke_override (const char *library_name, const char *entry_point_name) noexcept
{
- log_debug (LOG_ASSEMBLY, "[dynamic] clr_pinvoke_override (\"{}\", \"{}\")"sv, optional_string (library_name), optional_string (entry_point_name));
+ log_debug (LOG_ASSEMBLY, "[dynamic] clr_pinvoke_override (\"%s\", \"%s\")", optional_string (library_name), optional_string (entry_point_name));
void *ret = PinvokeOverride::monodroid_pinvoke_override (library_name, entry_point_name);
- log_debug (LOG_DEFAULT, "[dynamic] p/invoke {}found", ret == nullptr ? "not "sv : ""sv);
+ log_debug (LOG_DEFAULT, "[dynamic] p/invoke %sfound", ret == nullptr ? "not " : "");
return ret;
}
diff --git a/src/native/clr/pinvoke-override/precompiled.cc b/src/native/clr/pinvoke-override/precompiled.cc
index 25c63b53d45..c4c48615ff3 100644
--- a/src/native/clr/pinvoke-override/precompiled.cc
+++ b/src/native/clr/pinvoke-override/precompiled.cc
@@ -1,3 +1,5 @@
+#include
+
#define PINVOKE_OVERRIDE_INLINE [[gnu::always_inline]]
#include
@@ -22,12 +24,12 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons
PinvokeEntry *entry = find_pinvoke_address (entrypoint_hash, internal_pinvokes.data (), internal_pinvokes_count);
if (entry == nullptr) [[unlikely]] {
- log_fatal (LOG_ASSEMBLY, "Internal p/invoke symbol '{} @ {}' (hash: {:x}) not found in compile-time map."sv,
- optional_string (library_name), optional_string (entrypoint_name), entrypoint_hash);
+ log_fatal (LOG_ASSEMBLY, "Internal p/invoke symbol '%s @ %s' (hash: %zx) not found in compile-time map.",
+ optional_string (library_name), optional_string (entrypoint_name), static_cast(entrypoint_hash));
log_fatal (LOG_ASSEMBLY, "compile-time map contents:"sv);
for (size_t i = 0uz; i < internal_pinvokes_count; i++) {
PinvokeEntry const& e = internal_pinvokes[i];
- log_fatal (LOG_ASSEMBLY, "\t'{}'={:p} (hash: {:x})"sv, optional_string (e.name), e.func, e.hash);
+ log_fatal (LOG_ASSEMBLY, "\t'%s'=%p (hash: %zx)", optional_string (e.name), e.func, static_cast(e.hash));
}
Helpers::abort_application (
LOG_ASSEMBLY,
@@ -68,7 +70,7 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons
load_library_entry (library_name, entrypoint_name, *entry, dotnet_dso_handle);
if (entry->func == nullptr) {
- log_fatal (LOG_ASSEMBLY, "Failed to load symbol '{}' from shared library '{}'"sv,
+ log_fatal (LOG_ASSEMBLY, "Failed to load symbol '%s' from shared library '%s'",
optional_string (entrypoint_name), optional_string (library_name));
return nullptr; // let Mono deal with the fallout
}
@@ -79,7 +81,7 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons
// It's possible we don't have an entry for some `dotnet` p/invoke, fall back to the slow path below
log_debug (
LOG_ASSEMBLY,
- "Symbol '{}' in library '{}' not found in the generated tables, falling back to slow path"sv,
+ "Symbol '%s' in library '%s' not found in the generated tables, falling back to slow path",
optional_string (entrypoint_name),
optional_string (library_name)
);
@@ -100,8 +102,8 @@ auto PinvokeOverride::monodroid_pinvoke_override (const char *library_name, cons
const void* Host::clr_pinvoke_override (const char *library_name, const char *entry_point_name) noexcept
{
- log_debug (LOG_ASSEMBLY, "[precompiled] clr_pinvoke_override (\"{}\", \"{}\")"sv, library_name, entry_point_name);
+ log_debug (LOG_ASSEMBLY, "[precompiled] clr_pinvoke_override (\"%s\", \"%s\")", optional_string (library_name), optional_string (entry_point_name));
void *ret = PinvokeOverride::monodroid_pinvoke_override (library_name, entry_point_name);
- log_debug (LOG_DEFAULT, "[precompiled] p/invoke {}found"sv, ret == nullptr ? "not"sv : ""sv);
+ log_debug (LOG_DEFAULT, "[precompiled] p/invoke %sfound", ret == nullptr ? "not" : "");
return ret;
}
diff --git a/src/native/clr/runtime-base/android-system-shared.cc b/src/native/clr/runtime-base/android-system-shared.cc
index 2aa1d54b001..ed0c30b982a 100644
--- a/src/native/clr/runtime-base/android-system-shared.cc
+++ b/src/native/clr/runtime-base/android-system-shared.cc
@@ -1,3 +1,6 @@
+#include
+#include
+#include
#include
#include
@@ -36,15 +39,16 @@ AndroidSystem::monodroid__system_property_get (std::string_view const& name, cha
char *buf = nullptr;
if (sp_value_len < Constants::PROPERTY_VALUE_BUFFER_LEN) {
size_t alloc_size = Helpers::add_with_overflow_check (Constants::PROPERTY_VALUE_BUFFER_LEN, 1uz);
- log_warn (LOG_DEFAULT, "Buffer to store system property may be too small, will copy only {} bytes", sp_value_len);
- buf = new char [alloc_size];
+ log_write_fmt (LOG_DEFAULT, LogLevel::Warn, "Buffer to store system property may be too small, will copy only %zu bytes", sp_value_len);
+ buf = static_cast (std::malloc (alloc_size));
+ abort_unless (buf != nullptr, "Failed to allocate system property buffer");
}
int len = __system_property_get (name.data (), buf ? buf : sp_value);
if (buf != nullptr) {
strncpy (sp_value, buf, sp_value_len);
sp_value [sp_value_len] = '\0';
- delete[] buf;
+ std::free (buf);
}
return len;
@@ -81,10 +85,10 @@ AndroidSystem::get_max_gref_count_from_system () noexcept -> long
}
if (*e) {
- log_warn (LOG_GC, "Unsupported '{}' value '{}'.", Constants::DEBUG_MONO_MAX_GREFC.data (), override.get ());
+ log_write_fmt (LOG_GC, LogLevel::Warn, "Unsupported '%s' value '%s'.", Constants::DEBUG_MONO_MAX_GREFC.data (), override.get ());
}
- log_warn (LOG_GC, "Overriding max JNI Global Reference count to {}", max);
+ log_write_fmt (LOG_GC, LogLevel::Warn, "Overriding max JNI Global Reference count to %ld", max);
}
return max;
diff --git a/src/native/clr/runtime-base/android-system.cc b/src/native/clr/runtime-base/android-system.cc
index aee00900d55..b6c06e90a70 100644
--- a/src/native/clr/runtime-base/android-system.cc
+++ b/src/native/clr/runtime-base/android-system.cc
@@ -49,7 +49,7 @@ AndroidSystem::setup_environment (const char *name, const char *value) noexcept
if (isupper (name [0]) || name [0] == '_') {
if (setenv (name, v, 1) < 0) {
- log_warn (LOG_DEFAULT, "(Debug) Failed to set environment variable: {}", strerror (errno));
+ log_warn (LOG_DEFAULT, "(Debug) Failed to set environment variable: %s", strerror (errno));
}
return;
}
@@ -64,13 +64,13 @@ AndroidSystem::setup_environment_from_override_file (dynamic_local_string::max () && errno == ERANGE) || (buf[0] != '\0' && *endptr != '\0')) {
- log_warn (LOG_DEFAULT, "Malformed header of the environment override file {}: name width has invalid format", path.get ());
+ log_warn (LOG_DEFAULT, "Malformed header of the environment override file %s: name width has invalid format", path.get ());
return;
}
unsigned long value_width = strtoul (buf.get () + 11, &endptr, 16);
if ((value_width == std::numeric_limits::max () && errno == ERANGE) || (buf[0] != '\0' && *endptr != '\0')) {
- log_warn (LOG_DEFAULT, "Malformed header of the environment override file {}: value width has invalid format", path.get ());
+ log_warn (LOG_DEFAULT, "Malformed header of the environment override file %s: value width has invalid format", path.get ());
return;
}
uint64_t data_width = name_width + value_width;
if (data_width > file_size - Constants::OVERRIDE_ENVIRONMENT_FILE_HEADER_SIZE || (file_size - Constants::OVERRIDE_ENVIRONMENT_FILE_HEADER_SIZE) % data_width != 0) {
- log_warn (LOG_DEFAULT, "Malformed environment override file {}: invalid data size", path.get ());
+ log_warn (LOG_DEFAULT, "Malformed environment override file %s: invalid data size", path.get ());
return;
}
@@ -136,11 +136,11 @@ AndroidSystem::setup_environment_from_override_file (dynamic_local_string 0 && data_size >= data_width) {
if (*name == '\0') {
- log_warn (LOG_DEFAULT, "Malformed environment override file {}: name at offset {} is empty", path.get (), name - buf.get ());
+ log_warn (LOG_DEFAULT, "Malformed environment override file %s: name at offset %td is empty", path.get (), name - buf.get ());
return;
}
- log_debug (LOG_DEFAULT, "Setting environment variable from the override file {}: '{}' = '{}'", path.get (), name, name + name_width);
+ log_debug (LOG_DEFAULT, "Setting environment variable from the override file %s: '%s' = '%s'", path.get (), name, name + name_width);
setup_environment (name, name + name_width);
name += data_width;
data_size -= data_width;
@@ -161,7 +161,7 @@ AndroidSystem::add_apk_libdir (std::string_view const& apk, size_t &index, std::
dir.append (lib_prefix);
dir.append (abi);
app_lib_directories [index] = dir;
- log_debug (LOG_ASSEMBLY, "Added APK DSO lookup location: {}", dir);
+ log_debug (LOG_ASSEMBLY, "Added APK DSO lookup location: %s", dir.c_str ());
index++;
}
@@ -196,7 +196,7 @@ AndroidSystem::setup_apk_directories (unsigned short running_on_cpu, jstring_arr
add_apk_libdir (base_apk, number_of_added_directories, abi);
}
- log_debug (LOG_DEFAULT, "Number of added dirs: {}", number_of_added_directories);
+ log_debug (LOG_DEFAULT, "Number of added dirs: %zu", number_of_added_directories);
if (app_lib_directories.size () == number_of_added_directories) [[likely]] {
return;
}
@@ -213,7 +213,7 @@ AndroidSystem::setup_app_library_directories (jstring_array_wrapper& runtimeApks
app_lib_directories = std::span (single_app_lib_directory);
app_lib_directories [0] = std::string (appDirs[Constants::APP_DIRS_DATA_DIR_INDEX].get_cstr ());
- log_debug (LOG_ASSEMBLY, "Added filesystem DSO lookup location: {}", app_lib_directories [0]);
+ log_debug (LOG_ASSEMBLY, "Added filesystem DSO lookup location: %s", app_lib_directories [0].c_str ());
return;
}
@@ -237,7 +237,7 @@ void
AndroidSystem::setup_environment () noexcept
{
if (application_config.environment_variable_count > 0) {
- log_debug (LOG_DEFAULT, "Setting environment variables ({})", application_config.environment_variable_count);
+ log_debug (LOG_DEFAULT, "Setting environment variables (%u)", application_config.environment_variable_count);
HostEnvironment::set_values (
application_config.environment_variable_count,
app_environment_variables,
@@ -246,7 +246,7 @@ AndroidSystem::setup_environment () noexcept
}
if (application_config.system_property_count > 0) {
- log_debug (LOG_DEFAULT, "Setting system properties ({})", application_config.system_property_count);
+ log_debug (LOG_DEFAULT, "Setting system properties (%u)", application_config.system_property_count);
HostEnvironment::set_values (
application_config.system_property_count,
app_system_properties,
@@ -259,9 +259,9 @@ AndroidSystem::setup_environment () noexcept
dynamic_local_string env_override_file;
Util::path_combine (env_override_file, std::string_view {primary_override_dir}, Constants::OVERRIDE_ENVIRONMENT_FILE_NAME);
- log_debug (LOG_DEFAULT, "{}", env_override_file.get ());
+ log_debug (LOG_DEFAULT, "%s", env_override_file.get ());
if (Util::file_exists (env_override_file)) {
- log_debug (LOG_DEFAULT, "Loading {}"sv, env_override_file.get ());
+ log_debug (LOG_DEFAULT, "Loading %s", env_override_file.get ());
setup_environment_from_override_file (env_override_file);
}
#endif // def DEBUG
@@ -274,12 +274,12 @@ AndroidSystem::detect_embedded_dso_mode (jstring_array_wrapper& appDirs) noexcep
dynamic_local_string libmonodroid_path;
Util::path_combine (libmonodroid_path, appDirs[Constants::APP_DIRS_DATA_DIR_INDEX].get_string_view (), "libmonodroid.so"sv);
- log_debug (LOG_ASSEMBLY, "Checking if libmonodroid was unpacked to {}", libmonodroid_path.get ());
+ log_debug (LOG_ASSEMBLY, "Checking if libmonodroid was unpacked to %s", libmonodroid_path.get ());
if (!Util::file_exists (libmonodroid_path)) {
- log_debug (LOG_ASSEMBLY, "{} not found, assuming application/android:extractNativeLibs == false", libmonodroid_path.get ());
+ log_debug (LOG_ASSEMBLY, "%s not found, assuming application/android:extractNativeLibs == false", libmonodroid_path.get ());
set_embedded_dso_mode_enabled (true);
} else {
- log_debug (LOG_ASSEMBLY, "Native libs extracted to {}, assuming application/android:extractNativeLibs == true", appDirs[Constants::APP_DIRS_DATA_DIR_INDEX].get_cstr ());
+ log_debug (LOG_ASSEMBLY, "Native libs extracted to %s, assuming application/android:extractNativeLibs == true", appDirs[Constants::APP_DIRS_DATA_DIR_INDEX].get_cstr ());
set_embedded_dso_mode_enabled (false);
native_libraries_dir.assign (appDirs[Constants::APP_DIRS_DATA_DIR_INDEX].get_cstr ());
}
diff --git a/src/native/clr/runtime-base/logger.cc b/src/native/clr/runtime-base/logger.cc
index a61b68d4a76..0078aa7fe6a 100644
--- a/src/native/clr/runtime-base/logger.cc
+++ b/src/native/clr/runtime-base/logger.cc
@@ -1,10 +1,10 @@
#include
#include
#include
+#include
#include
#include
#include
-#include
#include
#include
@@ -22,10 +22,17 @@ using namespace xamarin::android;
using std::operator""sv;
namespace {
- std::string gref_file{};
- std::string lref_file{};
+ char *gref_file = nullptr;
+ char *lref_file = nullptr;
bool light_gref = false;
bool light_lref = false;
+
+ void set_log_file (char *&log_file, const char *path) noexcept
+ {
+ std::free (log_file);
+ log_file = path == nullptr ? nullptr : strdup (path);
+ abort_unless (path == nullptr || log_file != nullptr, "Failed to allocate reference log file path");
+ }
}
[[gnu::always_inline]]
@@ -52,7 +59,9 @@ auto Logger::open_file (LogCategories category, std::string_view const& custom_p
{
auto log_and_return = [&category](FILE *f, std::string_view const& path) -> FILE* {
if (f != nullptr) {
- log_debug (category, "Opened file '{}' for logging.", path);
+ if ((log_categories & category) != 0) {
+ log_write_fmt (category, LogLevel::Debug, "Opened file '%s' for logging.", path.data ());
+ }
}
return f;
};
@@ -62,28 +71,28 @@ auto Logger::open_file (LogCategories category, std::string_view const& custom_p
return log_and_return (ret, custom_path);
}
- std::string p{};
Util::create_public_directory (override_dir);
- p.assign (override_dir);
- p.append ("/");
- p.append (fallback_filename);
+ dynamic_local_string p;
+ p.append (override_dir)
+ .append ("/")
+ .append (fallback_filename);
- return log_and_return (open_file (p), p);
+ return log_and_return (open_file (p.get ()), p.get ());
}
void
Logger::init_reference_logging (std::string_view const& override_dir) noexcept
{
if ((log_categories & LOG_GREF) != 0 && !light_gref) {
- _gref_log = open_file (LOG_GREF, gref_file, override_dir, "grefs.txt"sv);
+ _gref_log = open_file (LOG_GREF, gref_file == nullptr ? std::string_view {} : std::string_view { gref_file }, override_dir, "grefs.txt"sv);
}
if ((log_categories & LOG_LREF) != 0 && !light_lref) {
// if both lref & gref have files specified, and they're the same path, reuse the FILE*.
- if (!lref_file.empty () && strcmp (lref_file.c_str (), !gref_file.empty () ? gref_file.c_str () : "") == 0) {
+ if (lref_file != nullptr && strcmp (lref_file, gref_file != nullptr ? gref_file : "") == 0) {
_lref_log = _gref_log;
} else {
- _lref_log = open_file (LOG_LREF, lref_file, override_dir, "lrefs.txt"sv);
+ _lref_log = open_file (LOG_LREF, lref_file == nullptr ? std::string_view {} : std::string_view { lref_file }, override_dir, "lrefs.txt"sv);
}
}
}
@@ -162,7 +171,16 @@ Logger::init_logging_categories () noexcept
auto file_name = segment.at (offset);
if (!file_name.has_value ()) {
- log_warn (LOG_DEFAULT, "Unable to set path to {} log file: {}", file_kind, to_string (file_name.error ()));
+ std::string_view error = to_string (file_name.error ());
+ log_write_fmt (
+ LOG_DEFAULT,
+ LogLevel::Warn,
+ "Unable to set path to %.*s log file: %.*s",
+ static_cast(file_kind.length ()),
+ file_kind.data (),
+ static_cast(error.length ()),
+ error.data ()
+ );
return nullptr;
}
@@ -171,7 +189,7 @@ Logger::init_logging_categories () noexcept
constexpr std::string_view CAT_GREF_EQUALS { "gref=" };
if (set_category (CAT_GREF_EQUALS, param, LOG_GREF, true /* arg_starts_with_name */)) {
- gref_file = get_log_file_name ("gref"sv, param, CAT_GREF_EQUALS.length ());
+ set_log_file (gref_file, get_log_file_name ("gref"sv, param, CAT_GREF_EQUALS.length ()));
continue;
}
@@ -187,7 +205,7 @@ Logger::init_logging_categories () noexcept
constexpr std::string_view CAT_LREF_EQUALS { "lref=" };
if (set_category (CAT_LREF_EQUALS, param, LOG_LREF, true /* arg_starts_with_name */)) {
- lref_file = get_log_file_name ("lref"sv, param, CAT_LREF_EQUALS.length ());
+ set_log_file (lref_file, get_log_file_name ("lref"sv, param, CAT_LREF_EQUALS.length ()));
continue;
}
diff --git a/src/native/clr/runtime-base/util.cc b/src/native/clr/runtime-base/util.cc
index 8f59681a55f..6b568921294 100644
--- a/src/native/clr/runtime-base/util.cc
+++ b/src/native/clr/runtime-base/util.cc
@@ -1,4 +1,5 @@
#include
+#include
#include
#include
@@ -58,7 +59,7 @@ Util::create_public_directory (std::string_view const& dir)
// Try to change the mode, just in case
chmod (dir.data (), 0777);
} else {
- log_warn (LOG_DEFAULT, "Failed to create directory '{}'. {}"sv, dir, std::strerror (errno));
+ log_write_fmt (LOG_DEFAULT, LogLevel::Warn, "Failed to create directory '%s'. %s", dir.data (), std::strerror (errno));
}
}
umask (m);
@@ -72,7 +73,7 @@ Util::monodroid_fopen (std::string_view const& filename, std::string_view const&
*/
FILE *ret = fopen (filename.data (), mode.data ());
if (ret == nullptr) {
- log_error (LOG_DEFAULT, "fopen failed for file {}: {}", filename, strerror (errno));
+ log_write_fmt (LOG_DEFAULT, LogLevel::Error, "fopen failed for file %s: %s", filename.data (), strerror (errno));
return nullptr;
}
@@ -87,7 +88,7 @@ void Util::set_world_accessable (std::string_view const& path)
} while (r == -1 && errno == EINTR);
if (r == -1) {
- log_error (LOG_DEFAULT, "chmod(\"{}\", 0664) failed: {}", path, strerror (errno));
+ log_write_fmt (LOG_DEFAULT, LogLevel::Error, "chmod(\"%s\", 0664) failed: %s", path.data (), strerror (errno));
}
}
@@ -99,7 +100,7 @@ auto Util::set_world_accessible (int fd) noexcept -> bool
} while (r == -1 && errno == EINTR);
if (r == -1) {
- log_error (LOG_DEFAULT, "fchmod() failed: {}"sv, strerror (errno));
+ log_write_fmt (LOG_DEFAULT, LogLevel::Error, "fchmod() failed: %s", strerror (errno));
return false;
}
diff --git a/src/native/clr/shared/helpers.cc b/src/native/clr/shared/helpers.cc
index 7850592af5a..e7d62723880 100644
--- a/src/native/clr/shared/helpers.cc
+++ b/src/native/clr/shared/helpers.cc
@@ -1,4 +1,5 @@
#include
+#include
#include
#include
@@ -7,11 +8,24 @@
using namespace xamarin::android;
+[[noreturn]] void
+Helpers::abort_applicationf (LogCategories category, std::source_location sloc, const char *format, ...) noexcept
+{
+ char message[512];
+ va_list args;
+ const char *safe_format = format == nullptr ? "" : format;
+ va_start (args, format);
+ vsnprintf (message, sizeof (message), safe_format, args);
+ va_end (args);
+
+ abort_application (category, message, true, sloc);
+}
+
[[noreturn]] void
Helpers::abort_application (LogCategories category, const char *message, bool log_location, std::source_location sloc) noexcept
{
// Log it, but also...
- log_fatal (category, "{}", message);
+ log_write (category, LogLevel::Fatal, message);
// ...let android include it in the tombstone, debuggerd output, stack trace etc
android_set_abort_message (message);
@@ -33,9 +47,10 @@ Helpers::abort_application (LogCategories category, const char *message, bool lo
}
}
- log_fatal (
+ log_write_fmt (
category,
- "Abort at {}:{}:{} ('{}')",
+ LogLevel::Fatal,
+ "Abort at %s:%u:%u ('%s')",
file_name,
sloc.line (),
sloc.column (),
diff --git a/src/native/clr/shared/log_functions.cc b/src/native/clr/shared/log_functions.cc
index acd5e705c06..ea94cf9016b 100644
--- a/src/native/clr/shared/log_functions.cc
+++ b/src/native/clr/shared/log_functions.cc
@@ -128,4 +128,74 @@ namespace xamarin::android {
__android_log_write (priority, category_name (category), message);
}
+
+ void
+ log_writev (LogCategories category, LogLevel level, const char *format, va_list args) noexcept
+ {
+ size_t map_index = static_cast