From 2688394249b3eabbd0eb0ea0f8b31a52e4147a16 Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Thu, 5 Mar 2026 12:31:16 +0100 Subject: [PATCH 1/2] Add CoreCLR debugger support for Android - Introduced targets for setting up and running the CoreCLR remote debugger. - Added conditions to enable CoreCLR debugging based on project properties. - Updated environment variables for CoreCLR profiling. - Implemented tests to verify CoreCLR debugger environment variables and behavior in release builds. --- .../Microsoft.Android.Sdk.Application.targets | 60 +++++++++++++++++++ ...oft.Android.Sdk.AssemblyResolution.targets | 15 ++++- ...soft.Android.Sdk.DefaultProperties.targets | 11 +++- .../GenerateNativeApplicationConfigSources.cs | 14 ++++- .../EnvironmentContentTests.cs | 59 ++++++++++++++++++ .../Utilities/EnvironmentBuilder.cs | 7 +++ .../Xamarin.Android.Common.targets | 6 ++ 7 files changed, 165 insertions(+), 7 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Application.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Application.targets index a4c9d65f801..677d2c607c2 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Application.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Application.targets @@ -198,4 +198,64 @@ This file contains targets specific for Android application projects. /> + + + + + + + + + + + + + + + + + + + + <_AmStartUserArg Condition=" '$(AndroidDeviceUserId)' != '' "> --user $(AndroidDeviceUserId) + + + + + + diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets index b1717d2b0fe..1d4197ba533 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets @@ -239,11 +239,13 @@ _ResolveAssemblies MSBuild target. <_ResolvedNativeLibraries Include="@(ResolvedFileToPublish)" Condition=" '%(ResolvedFileToPublish.Extension)' == '.so' " /> - + <_MonoComponent Condition=" '$(AndroidEnableProfiler)' == 'true' " Include="diagnostics_tracing" /> <_MonoComponent Condition=" '$(AndroidUseInterpreter)' == 'true' " Include="hot_reload" /> <_MonoComponent Condition=" '$(AndroidIncludeDebugSymbols)' == 'true' " Include="debugger" /> <_MonoComponent Condition=" '$(_AndroidExcludeMarshalIlgenComponent)' != 'true' " Include="marshal-ilgen" /> + + <_ExcludedNativeLibraries Condition=" '$(_AndroidIncludeSystemGlobalizationNative)' != 'true' " Include="libSystem.Globalization.Native" /> <_ExcludedNativeLibraries Condition=" '$(_AndroidEnableNativeStackTracing)' != 'true' " Include="libxamarin-native-tracing" /> @@ -274,6 +276,17 @@ _ResolveAssemblies MSBuild target. KnownRuntimeNativeLibraries="@(_KnownRuntimeNativeLibrary)"> + + + + arm64-v8a + + + x86_64 + + diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets index e9c75e1667d..29890bb8e07 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets @@ -21,9 +21,6 @@ <_AndroidFastDeploymentSupported Condition=" Exists ('$(MSBuildThisFileDirectory)../tools/Xamarin.Android.Common.Debugging.targets') ">true <_AndroidFastDeploymentSupported Condition=" '$(_AndroidFastDeploymentSupported)' == '' ">False - - true - True + + True diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs index 81a4ef23e30..0b134882224 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs @@ -72,6 +72,7 @@ public class GenerateNativeApplicationConfigSources : AndroidTask public string? AndroidSequencePointsMode { get; set; } public bool EnableSGenConcurrent { get; set; } public string? CustomBundleConfigFile { get; set; } + public bool EnableCoreClrDebugger { get; set; } bool _Debug { get { @@ -118,13 +119,20 @@ public override bool RunTask () envBuilder.AddDefaultDebugBuildLogLevel (); } - if (androidRuntime != Xamarin.Android.Tasks.AndroidRuntime.NativeAOT) { - AddDefaultEnvironmentVariables (envBuilder, HttpClientHandlerType, EnableSGenConcurrent); - } else { + if (androidRuntime == Xamarin.Android.Tasks.AndroidRuntime.NativeAOT) { // NativeAOT sets all the environment variables from Java, we don't want to repeat that // process in the native code. This is just a precaution, because NativeAOT builds should // not even use this task. envBuilder.EnvironmentVariables.Clear (); + } else if (androidRuntime == Xamarin.Android.Tasks.AndroidRuntime.CoreCLR) { + // CoreCLR needs the HTTP client handler type + envBuilder.AddHttpClientHandlerType (HttpClientHandlerType); + } else { + AddDefaultEnvironmentVariables (envBuilder, HttpClientHandlerType, EnableSGenConcurrent); + } + + if (EnableCoreClrDebugger && TargetsCLR) { + envBuilder.AddCoreClrDebuggerEnvironment (); } global::Android.Runtime.BoundExceptionType boundExceptionType; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/EnvironmentContentTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/EnvironmentContentTests.cs index 1f5d51ef909..8c82b56e0b3 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/EnvironmentContentTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/EnvironmentContentTests.cs @@ -237,5 +237,64 @@ public void CheckHttpClientHandlerType ([Values] AndroidRuntime runtime) Assert.AreEqual (expectedUpdatedValue, envvars[httpClientHandlerVarName]); } } + + [Test] + public void CheckCoreClrDebuggerEnvironmentVariables () + { + const string supportedAbis = "arm64-v8a;x86_64"; + + var proj = new XamarinAndroidApplicationProject () { + IsRelease = false, + }; + + proj.SetRuntime (AndroidRuntime.CoreCLR); + proj.SetProperty ("AndroidEnableCoreClrDebugger", "true"); + proj.SetAndroidSupportedAbis (supportedAbis); + + using (var b = CreateApkBuilder ()) { + Assert.IsTrue (b.Build (proj), "Build should have succeeded."); + + string intermediateOutputDir = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath); + List envFiles = EnvironmentHelper.GatherEnvironmentFiles (intermediateOutputDir, supportedAbis, true, AndroidRuntime.CoreCLR); + Dictionary envvars = EnvironmentHelper.ReadEnvironmentVariables (envFiles, AndroidRuntime.CoreCLR); + Assert.IsTrue (envvars.Count > 0, $"No environment variables defined"); + + Assert.IsTrue (envvars.ContainsKey ("CORECLR_ENABLE_PROFILING"), "Environment should contain CORECLR_ENABLE_PROFILING"); + Assert.AreEqual ("1", envvars ["CORECLR_ENABLE_PROFILING"], "CORECLR_ENABLE_PROFILING should be '1'"); + + Assert.IsTrue (envvars.ContainsKey ("CORECLR_PROFILER"), "Environment should contain CORECLR_PROFILER"); + Assert.AreEqual ("{9DC623E8-C88F-4FD5-AD99-77E67E1D9631}", envvars ["CORECLR_PROFILER"], "CORECLR_PROFILER GUID mismatch"); + + Assert.IsTrue (envvars.ContainsKey ("CORECLR_PROFILER_PATH"), "Environment should contain CORECLR_PROFILER_PATH"); + Assert.AreEqual ("libremotemscordbitarget.so", envvars ["CORECLR_PROFILER_PATH"], "CORECLR_PROFILER_PATH should point to libremotemscordbitarget.so"); + + Assert.IsFalse (envvars.ContainsKey ("MONO_GC_PARAMS"), "CoreCLR builds should not set MONO_GC_PARAMS"); + Assert.IsFalse (envvars.ContainsKey ("MONO_DEBUG"), "CoreCLR builds should not set MONO_DEBUG"); + } + } + + [Test] + public void CheckCoreClrDebuggerNotEnabledInRelease () + { + const string supportedAbis = "arm64-v8a"; + + var proj = new XamarinAndroidApplicationProject () { + IsRelease = true, + }; + + proj.SetRuntime (AndroidRuntime.CoreCLR); + proj.SetAndroidSupportedAbis (supportedAbis); + + using (var b = CreateApkBuilder ()) { + Assert.IsTrue (b.Build (proj), "Build should have succeeded."); + + string intermediateOutputDir = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath); + List envFiles = EnvironmentHelper.GatherEnvironmentFiles (intermediateOutputDir, supportedAbis, true, AndroidRuntime.CoreCLR); + Dictionary envvars = EnvironmentHelper.ReadEnvironmentVariables (envFiles, AndroidRuntime.CoreCLR); + + Assert.IsFalse (envvars.ContainsKey ("CORECLR_ENABLE_PROFILING"), "Release builds should not enable CoreCLR profiling"); + Assert.IsFalse (envvars.ContainsKey ("CORECLR_PROFILER"), "Release builds should not set CORECLR_PROFILER"); + } + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/EnvironmentBuilder.cs b/src/Xamarin.Android.Build.Tasks/Utilities/EnvironmentBuilder.cs index 51121c052bf..8006c95a7ce 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/EnvironmentBuilder.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/EnvironmentBuilder.cs @@ -108,5 +108,12 @@ public void AddMonoGcParams (bool enableSgenConcurrent) AddEnvironmentVariable ("MONO_GC_PARAMS", enableSgenConcurrent ? "major=marksweep-conc" : "major=marksweep"); } + public void AddCoreClrDebuggerEnvironment () + { + AddEnvironmentVariable ("CORECLR_ENABLE_PROFILING", "1"); + AddEnvironmentVariable ("CORECLR_PROFILER", "{9DC623E8-C88F-4FD5-AD99-77E67E1D9631}"); + AddEnvironmentVariable ("CORECLR_PROFILER_PATH", "libremotemscordbitarget.so"); + } + static string ValidAssemblerString (string s) => s.Replace ("\\", "\\\\").Replace ("\"", "\\\""); } diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 2b0c321c53a..340b7212191 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1715,6 +1715,11 @@ because xbuild doesn't support framework reference assemblies. <_AllNativeLibraries Include="@(AndroidNativeLibrary);@(EmbeddedNativeLibrary);@(FrameworkNativeLibrary)" /> + + + <_AndroidNativeLibraryNeverJniPreload Include="libremotemscordbitarget.so" /> + + @@ -1751,6 +1756,7 @@ because xbuild doesn't support framework reference assemblies. TargetsCLR="$(_AndroidUseCLR)" AndroidRuntime="$(_AndroidRuntime)" ProjectRuntimeConfigFilePath="$(ProjectRuntimeConfigFilePath)" + EnableCoreClrDebugger="$(AndroidEnableCoreClrDebugger)" > From b378fbc8987a5219cdec182a884752bf0cbd9250 Mon Sep 17 00:00:00 2001 From: Milos Kotlar Date: Thu, 12 Mar 2026 10:23:47 +0100 Subject: [PATCH 2/2] Refactor CoreCLR debugger support to use generic debugger properties --- .../Microsoft.Android.Sdk.Application.targets | 60 ------------------- ...oft.Android.Sdk.AssemblyResolution.targets | 10 ++-- ...soft.Android.Sdk.DefaultProperties.targets | 11 ++-- .../GenerateNativeApplicationConfigSources.cs | 6 +- .../EnvironmentContentTests.cs | 58 +++++++++++++++++- .../Utilities/EnvironmentBuilder.cs | 2 +- .../Xamarin.Android.Common.targets | 4 +- 7 files changed, 71 insertions(+), 80 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Application.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Application.targets index 677d2c607c2..a4c9d65f801 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Application.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Application.targets @@ -198,64 +198,4 @@ This file contains targets specific for Android application projects. /> - - - - - - - - - - - - - - - - - - - - <_AmStartUserArg Condition=" '$(AndroidDeviceUserId)' != '' "> --user $(AndroidDeviceUserId) - - - - - - diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets index 1d4197ba533..f69a7ec4214 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets @@ -277,13 +277,13 @@ _ResolveAssemblies MSBuild target. - - + + arm64-v8a - + x86_64 diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets index 29890bb8e07..2a13ef1a3f0 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets @@ -54,11 +54,10 @@ true false - true - false - 4711 - 1 - $(MSBuildThisFileDirectory)..\tools\libremotemscordbitarget + true + false + 4711 + $(MSBuildThisFileDirectory)..\tools\libremotemscordbitarget True - True + True diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs index 0b134882224..bbb3b6ac654 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs @@ -72,7 +72,7 @@ public class GenerateNativeApplicationConfigSources : AndroidTask public string? AndroidSequencePointsMode { get; set; } public bool EnableSGenConcurrent { get; set; } public string? CustomBundleConfigFile { get; set; } - public bool EnableCoreClrDebugger { get; set; } + public bool EnableDebugger { get; set; } bool _Debug { get { @@ -131,8 +131,8 @@ public override bool RunTask () AddDefaultEnvironmentVariables (envBuilder, HttpClientHandlerType, EnableSGenConcurrent); } - if (EnableCoreClrDebugger && TargetsCLR) { - envBuilder.AddCoreClrDebuggerEnvironment (); + if (EnableDebugger && TargetsCLR) { + envBuilder.AddDebuggerEnvironment (); } global::Android.Runtime.BoundExceptionType boundExceptionType; diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/EnvironmentContentTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/EnvironmentContentTests.cs index 8c82b56e0b3..fbb82251eea 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/EnvironmentContentTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/EnvironmentContentTests.cs @@ -239,7 +239,7 @@ public void CheckHttpClientHandlerType ([Values] AndroidRuntime runtime) } [Test] - public void CheckCoreClrDebuggerEnvironmentVariables () + public void CheckDebuggerEnvironmentVariables () { const string supportedAbis = "arm64-v8a;x86_64"; @@ -248,7 +248,7 @@ public void CheckCoreClrDebuggerEnvironmentVariables () }; proj.SetRuntime (AndroidRuntime.CoreCLR); - proj.SetProperty ("AndroidEnableCoreClrDebugger", "true"); + proj.SetProperty ("AndroidEnableDebugger", "true"); proj.SetAndroidSupportedAbis (supportedAbis); using (var b = CreateApkBuilder ()) { @@ -274,7 +274,59 @@ public void CheckCoreClrDebuggerEnvironmentVariables () } [Test] - public void CheckCoreClrDebuggerNotEnabledInRelease () + public void CheckDebuggerNativeLibraryInApk () + { + const string supportedAbis = "arm64-v8a;x86_64"; + + var proj = new XamarinAndroidApplicationProject () { + IsRelease = false, + }; + + proj.SetRuntime (AndroidRuntime.CoreCLR); + proj.SetProperty ("AndroidEnableDebugger", "true"); + proj.SetAndroidSupportedAbis (supportedAbis); + + using (var b = CreateApkBuilder ()) { + Assert.IsTrue (b.Build (proj), "Build should have succeeded."); + + string apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk"); + Assert.IsTrue (File.Exists (apk), $"APK not found at {apk}"); + + foreach (var abi in supportedAbis.Split (';')) { + string entryPath = $"lib/{abi}/libremotemscordbitarget.so"; + var data = ZipHelper.ReadFileFromZip (apk, entryPath); + Assert.IsNotNull (data, $"{entryPath} should be present in the APK"); + Assert.IsTrue (data.Length > 0, $"{entryPath} should not be empty"); + } + } + } + + [Test] + public void CheckDebuggerNativeLibraryNotInReleaseApk () + { + const string supportedAbis = "arm64-v8a"; + + var proj = new XamarinAndroidApplicationProject () { + IsRelease = true, + }; + + proj.SetRuntime (AndroidRuntime.CoreCLR); + proj.SetAndroidSupportedAbis (supportedAbis); + + using (var b = CreateApkBuilder ()) { + Assert.IsTrue (b.Build (proj), "Build should have succeeded."); + + string apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, $"{proj.PackageName}-Signed.apk"); + Assert.IsTrue (File.Exists (apk), $"APK not found at {apk}"); + + string entryPath = $"lib/{supportedAbis}/libremotemscordbitarget.so"; + var data = ZipHelper.ReadFileFromZip (apk, entryPath); + Assert.IsNull (data, $"{entryPath} should NOT be present in Release APK"); + } + } + + [Test] + public void CheckDebuggerNotEnabledInRelease () { const string supportedAbis = "arm64-v8a"; diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/EnvironmentBuilder.cs b/src/Xamarin.Android.Build.Tasks/Utilities/EnvironmentBuilder.cs index 8006c95a7ce..324ef330af4 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/EnvironmentBuilder.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/EnvironmentBuilder.cs @@ -108,7 +108,7 @@ public void AddMonoGcParams (bool enableSgenConcurrent) AddEnvironmentVariable ("MONO_GC_PARAMS", enableSgenConcurrent ? "major=marksweep-conc" : "major=marksweep"); } - public void AddCoreClrDebuggerEnvironment () + public void AddDebuggerEnvironment () { AddEnvironmentVariable ("CORECLR_ENABLE_PROFILING", "1"); AddEnvironmentVariable ("CORECLR_PROFILER", "{9DC623E8-C88F-4FD5-AD99-77E67E1D9631}"); diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 340b7212191..efc58817eb3 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1715,7 +1715,7 @@ because xbuild doesn't support framework reference assemblies. <_AllNativeLibraries Include="@(AndroidNativeLibrary);@(EmbeddedNativeLibrary);@(FrameworkNativeLibrary)" /> - + <_AndroidNativeLibraryNeverJniPreload Include="libremotemscordbitarget.so" /> @@ -1756,7 +1756,7 @@ because xbuild doesn't support framework reference assemblies. TargetsCLR="$(_AndroidUseCLR)" AndroidRuntime="$(_AndroidRuntime)" ProjectRuntimeConfigFilePath="$(ProjectRuntimeConfigFilePath)" - EnableCoreClrDebugger="$(AndroidEnableCoreClrDebugger)" + EnableDebugger="$(AndroidEnableDebugger)" >