diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/DiagnosticSettings.cs b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/DiagnosticSettings.cs new file mode 100644 index 00000000000..3731ec693c1 --- /dev/null +++ b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/DiagnosticSettings.cs @@ -0,0 +1,136 @@ +using System.IO; +using System.Text; +using System.Runtime.InteropServices; + +using Java.Interop; + +namespace Microsoft.Android.Runtime; + +struct DiagnosticSettings { + + public bool LogJniLocalReferences; + private string? LrefPath; + + public bool LogJniGlobalReferences; + private string? GrefPath; + + private TextWriter? GrefLrefLog; + + + public TextWriter? GrefLog { + get { + if (!LogJniGlobalReferences) { + return null; + } + return ((LrefPath != null && LrefPath == GrefPath) + ? GrefLrefLog ??= CreateWriter (LrefPath) + : null) + ?? + ((GrefPath != null) + ? CreateWriter (GrefPath) + : null) + ?? + new LogcatTextWriter (AndroidLogLevel.Debug, "NativeAot:GREF"); + } + } + + public TextWriter? LrefLog { + get { + if (!LogJniLocalReferences) { + return null; + } + return ((LrefPath != null && LrefPath == GrefPath) + ? GrefLrefLog ??= CreateWriter (LrefPath) + : null) + ?? + ((LrefPath != null) + ? CreateWriter (LrefPath) + : null) + ?? + new LogcatTextWriter (AndroidLogLevel.Debug, "NativeAot:LREF"); + } + } + + TextWriter? CreateWriter (string path) + { + try { + return File.CreateText (path); + } + catch (Exception e) { + AndroidLog.Print (AndroidLogLevel.Error, "NativeAot", $"Failed to open log file `{path}`: {e}"); + return null; + } + } + + public void AddDebugDotnetLog () + { + Span value = stackalloc byte [RuntimeNativeMethods.PROP_VALUE_MAX]; + if (!RuntimeNativeMethods.TryGetSystemProperty ("debug.dotnet.log"u8, ref value)) { + return; + } + AddParse (value); + } + + void AddParse (ReadOnlySpan value) + { + while (TryGetNextValue (ref value, out var v)) { + if (v.SequenceEqual ("lref"u8)) { + LogJniLocalReferences = true; + } + else if (v.StartsWith ("lref="u8)) { + LogJniLocalReferences = true; + var path = v.Slice ("lref=".Length); + LrefPath = Encoding.UTF8.GetString (path); + } + else if (v.SequenceEqual ("gref"u8)) { + LogJniGlobalReferences = true; + } + else if (v.StartsWith ("gref="u8)) { + LogJniGlobalReferences = true; + var path = v.Slice ("gref=".Length); + GrefPath = Encoding.UTF8.GetString (path); + } + else if (v.SequenceEqual ("all"u8)) { + LogJniLocalReferences = true; + LogJniGlobalReferences = true; + } + } + + bool TryGetNextValue (ref ReadOnlySpan value, out ReadOnlySpan next) + { + if (value.Length == 0) { + next = default; + return false; + } + int c = value.IndexOf ((byte) ','); + if (c >= 0) { + next = value.Slice (0, c); + value = value.Slice (c + 1); + } + else { + next = value; + value = default; + } + return true; + } + } +} + +static partial class RuntimeNativeMethods { + + [LibraryImport ("c", EntryPoint="__system_property_get")] + static private partial int system_property_get (ReadOnlySpan name, Span value); + + internal const int PROP_VALUE_MAX = 92; + + internal static bool TryGetSystemProperty (ReadOnlySpan name, ref Span value) + { + int len = system_property_get (name, value); + if (len <= 0) { + return false; + } + + value = value.Slice (0, len); + return true; + } +} diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs index 869d82b1cd5..1d0aae0ff41 100644 --- a/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs +++ b/src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs @@ -4,7 +4,7 @@ namespace Microsoft.Android.Runtime; -static class JavaInteropRuntime +static partial class JavaInteropRuntime { static JniRuntime? runtime; @@ -34,14 +34,17 @@ static void JNI_OnUnload (IntPtr vm, IntPtr reserved) static void init (IntPtr jnienv, IntPtr klass) { try { + var settings = new DiagnosticSettings (); + settings.AddDebugDotnetLog (); + var typeManager = new NativeAotTypeManager (); var options = new NativeAotRuntimeOptions { EnvironmentPointer = jnienv, TypeManager = typeManager, ValueManager = new NativeAotValueManager (typeManager), UseMarshalMemberBuilder = false, - JniGlobalReferenceLogWriter = new LogcatTextWriter (AndroidLogLevel.Debug, "NativeAot:GREF"), - JniLocalReferenceLogWriter = new LogcatTextWriter (AndroidLogLevel.Debug, "NativeAot:LREF"), + JniGlobalReferenceLogWriter = settings.GrefLog, + JniLocalReferenceLogWriter = settings.LrefLog, }; runtime = options.CreateJreVM (); diff --git a/src/Microsoft.Android.Runtime.NativeAOT/Microsoft.Android.Runtime.NativeAOT.csproj b/src/Microsoft.Android.Runtime.NativeAOT/Microsoft.Android.Runtime.NativeAOT.csproj index 5ae08596c98..44045665873 100644 --- a/src/Microsoft.Android.Runtime.NativeAOT/Microsoft.Android.Runtime.NativeAOT.csproj +++ b/src/Microsoft.Android.Runtime.NativeAOT/Microsoft.Android.Runtime.NativeAOT.csproj @@ -11,6 +11,7 @@ true $(_MonoAndroidNETDefaultOutDir) Microsoft.Android.Runtime + true