From 630319166f65ebb6e3a4b6993354a36224fd4f0b Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Wed, 2 Oct 2019 09:27:11 +0200 Subject: [PATCH] [WIP] JNIEnv.Initialize optimization --- src/Mono.Android/Android.Runtime/JNIEnv.cs | 122 +++++-------- src/Mono.Android/Android.Runtime/Logger.cs | 33 ++-- src/Mono.Android/Java.Interop/TypeManager.cs | 28 +-- .../Tasks/GeneratePackageManagerJava.cs | 19 ++- ...pplicationConfigNativeAssemblyGenerator.cs | 10 ++ .../Xamarin.Android.Common.targets | 101 +++++------ src/monodroid/jni/application_dso_stub.cc | 2 + src/monodroid/jni/globals.cc | 1 + src/monodroid/jni/globals.hh | 3 + src/monodroid/jni/logger.cc | 60 +++++++ src/monodroid/jni/monodroid-glue.cc | 62 +++++-- src/monodroid/jni/timing.hh | 161 ++++++++++++++++++ src/monodroid/jni/util.hh | 35 ---- src/monodroid/jni/xamarin-app.h | 2 + 14 files changed, 418 insertions(+), 221 deletions(-) create mode 100644 src/monodroid/jni/timing.hh diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs index 6555ed732fe..12aebdaf58e 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnv.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs @@ -29,7 +29,9 @@ struct JnienvInitializeArgs { public int localRefsAreIndirect; public int grefGcThreshold; public IntPtr grefIGCUserPeer; - public int isRunningOnDesktop; + public byte isRunningOnDesktop; + public byte brokenExceptionTransitions; + public PackageNamingPolicy packageNamingPolicy; } public static partial class JNIEnv { @@ -49,7 +51,7 @@ public static partial class JNIEnv { internal static int gref_gc_threshold; internal static IntPtr mid_Class_getName; - + internal static bool PropagateExceptions; static UncaughtExceptionHandler defaultUncaughtExceptionHandler; @@ -64,7 +66,7 @@ public static partial class JNIEnv { [ThreadStatic] static JniNativeInterfaceInvoker env; static JniNativeInterfaceInvoker Env { - get { + get { if (Handle == IntPtr.Zero) // Forces thread attach if necessary. throw new Exception ("JNIEnv handle is NULL"); return env; @@ -115,53 +117,42 @@ public static void CheckHandle (IntPtr jnienv) static unsafe void RegisterJniNatives (IntPtr typeName_ptr, int typeName_len, IntPtr jniClass, IntPtr methods_ptr, int methods_len) { string typeName = new string ((char*) typeName_ptr, 0, typeName_len); - - var __start = new DateTime (); - if (Logger.LogTiming) { - __start = DateTime.UtcNow; - Logger.Log (LogLevel.Info, - "monodroid-timing", - "JNIEnv.RegisterJniNatives (\"" + typeName + "\", 0x" + jniClass.ToString ("x") + ") start: " + (__start - new DateTime (1970, 1, 1)).TotalMilliseconds); - } - Type type = Type.GetType (typeName); if (type == null) { Logger.Log (LogLevel.Error, "MonoDroid", - "Could not load type '" + typeName + "'. Skipping JNI registration of type '" + + "Could not load type '" + typeName + "'. Skipping JNI registration of type '" + Java.Interop.TypeManager.GetClassName (jniClass) + "'."); return; } - var className = Java.Interop.TypeManager.GetClassName (jniClass); + var className = Java.Interop.TypeManager.GetClassName (jniClass); // TODO: <- replace contents of GetClassName with p/invokes TypeManager.RegisterType (className, type); JniType jniType = null; JniType.GetCachedJniType (ref jniType, className); androidRuntime.TypeManager.RegisterNativeMembers (jniType, type, methods_ptr == IntPtr.Zero ? null : new string ((char*) methods_ptr, 0, methods_len)); - - if (Logger.LogTiming) { - var __end = DateTime.UtcNow; - Logger.Log (LogLevel.Info, - "monodroid-timing", - "JNIEnv.RegisterJniNatives total time: " + (__end - new DateTime (1970, 1, 1)).TotalMilliseconds + " [elapsed: " + (__end - __start).TotalMilliseconds + " ms]"); - } } + [DllImport ("__Internal")] + extern static void monodroid_log (LogLevel level, LogCategories category, string message); + + [DllImport ("__Internal")] + extern static IntPtr monodroid_timing_start (string message); + + [DllImport ("__Internal")] + extern static void monodroid_timing_stop (IntPtr sequence, string message); + internal static unsafe void Initialize (JnienvInitializeArgs* args) { - Logger.Categories = (LogCategories) args->logCategories; - - Stopwatch stopper = null; - long elapsed, totalElapsed = 0; - if (Logger.LogTiming) { - stopper = new Stopwatch (); - stopper.Start (); - Logger.Log (LogLevel.Info, "monodroid-timing", "JNIEnv.Initialize start"); - elapsed = stopper.ElapsedMilliseconds; - totalElapsed += elapsed; - Logger.Log (LogLevel.Info, "monodroid-timing", $"JNIEnv.Initialize: Logger JIT/etc. time: elapsed {elapsed} ms]"); - stopper.Restart (); + // TODO: access to args fields (some of them - analysis needed) should be lazy - p/invokes, with locking or atomic operations or TLS (test times!) + + bool logTiming = (args->logCategories & (uint)LogCategories.Timing) != 0; + IntPtr total_timing_sequence = IntPtr.Zero; + IntPtr partial_timing_sequence = IntPtr.Zero; + if (logTiming) { + total_timing_sequence = monodroid_timing_start ("JNIEnv.Initialize start"); + partial_timing_sequence = monodroid_timing_start (null); } gref_gc_threshold = args->grefGcThreshold; @@ -170,13 +161,6 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) java_vm = args->javaVm; -#if !JAVA_INTEROP - handle = args->env; - env = CreateNativeInterface (); - - invoke_iface = (JNIInvokeInterface) Marshal.PtrToStructure (Marshal.ReadIntPtr (java_vm), typeof (JNIInvokeInterface)); -#endif // !JAVA_INTEROP - version = args->version; androidSdkVersion = args->androidSdkVersion; @@ -193,60 +177,34 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) Mono.SystemDependencyProvider.Initialize (); -#if JAVA_INTEROP + // TODO: env, javaVm, grefLoader, Loader_loadClass should all be obtained by AndroidRuntime constructor with p/invokes + // TODO: consider removing AndroidRuntime instantiation here androidRuntime = new AndroidRuntime (args->env, args->javaVm, androidSdkVersion > 10, args->grefLoader, args->Loader_loadClass); -#endif // JAVA_INTEROP - if (Logger.LogTiming) { - elapsed = stopper.ElapsedMilliseconds; - totalElapsed += elapsed; - Logger.Log (LogLevel.Info, "monodroid-timing", $"JNIEnv.Initialize: managed runtime init time: elapsed {elapsed} ms]"); - stopper.Restart (); + if (logTiming) { + monodroid_timing_stop (partial_timing_sequence, "JNIEnv.Initialize: managed runtime init time"); + partial_timing_sequence = monodroid_timing_start (null); var _ = Java.Interop.TypeManager.jniToManaged; - elapsed = stopper.ElapsedMilliseconds; - totalElapsed += elapsed; - Logger.Log (LogLevel.Info, "monodroid-timing", $"JNIEnv.Initialize: TypeManager init time: elapsed {elapsed} ms]"); + monodroid_timing_stop (partial_timing_sequence, "JNIEnv.Initialize: TypeManager init time"); } AllocObjectSupported = androidSdkVersion > 10; - IsRunningOnDesktop = Convert.ToBoolean (args->isRunningOnDesktop); + IsRunningOnDesktop = args->isRunningOnDesktop == 1; Java.Interop.Runtime.grefIGCUserPeer_class = args->grefIGCUserPeer; -#if BROKEN_EXCEPTION_TRANSITIONS // XA < 5.0 - var propagate = - Environment.GetEnvironmentVariable ("__XA_PROPAGATE_EXCEPTIONS__") ?? - Environment.GetEnvironmentVariable ("__XA_PROPOGATE_EXCEPTIONS__"); - if (!string.IsNullOrEmpty (propagate)) { - bool.TryParse (propagate, out PropagateExceptions); - } - if (PropagateExceptions) { - Logger.Log (LogLevel.Info, - "monodroid", - "Enabling managed-to-java exception propagation."); - } -#else - PropagateExceptions = true; - var brokenExceptionTransitions = Environment.GetEnvironmentVariable ("XA_BROKEN_EXCEPTION_TRANSITIONS"); - bool brokenTransitions; - if (!string.IsNullOrEmpty (brokenExceptionTransitions) && bool.TryParse (brokenExceptionTransitions, out brokenTransitions)) { - PropagateExceptions = !brokenTransitions; - } -#endif + PropagateExceptions = args->brokenExceptionTransitions == 0; + if (PropagateExceptions) { defaultUncaughtExceptionHandler = new UncaughtExceptionHandler (Java.Lang.Thread.DefaultUncaughtExceptionHandler); if (!IsRunningOnDesktop) Java.Lang.Thread.DefaultUncaughtExceptionHandler = defaultUncaughtExceptionHandler; } - var packageNamingPolicy = Environment.GetEnvironmentVariable ("__XA_PACKAGE_NAMING_POLICY__"); - if (Enum.TryParse (packageNamingPolicy, out PackageNamingPolicy pnp)) { - JavaNativeTypeManager.PackageNamingPolicy = pnp; - } + JavaNativeTypeManager.PackageNamingPolicy = (PackageNamingPolicy)args->packageNamingPolicy; - if (Logger.LogTiming) { - totalElapsed += stopper.ElapsedMilliseconds; - Logger.Log (LogLevel.Info, "monodroid-timing", $"JNIEnv.Initialize end: elapsed {totalElapsed} ms"); + if (logTiming) { + monodroid_timing_stop (total_timing_sequence, "JNIEnv.Initialize end"); } } @@ -888,7 +846,7 @@ public static string GetCharSequence (IntPtr jobject, JniHandleOwnership transfe var r = JniEnvironment.Object.ToString (new JniObjectReference (jobject)); return JniEnvironment.Strings.ToString (ref r, JniObjectReferenceOptions.CopyAndDispose); #else // !JAVA_INTEROP - IntPtr str = LogCreateLocalRef (Env.CallObjectMethod (Handle, jobject, Java.Lang.Class.CharSequence_toString)); + IntPtr str = LogCreateLocalRef (Env.CallObjectMethod (Handle, jobject, Java.Lang.Class.CharSequence_toString)); try { return GetString (str, JniHandleOwnership.TransferLocalRef); } finally { @@ -2031,7 +1989,7 @@ struct JNIInvokeInterface { public IntPtr reserved0; public IntPtr reserved1; public IntPtr reserved2; - + public IntPtr DestroyJavaVM; // jint (*DestroyJavaVM)(JavaVM*); public AttachCurrentThreadDelegate AttachCurrentThread; public DetachCurrentThreadDelegate DetachCurrentThread; @@ -2051,7 +2009,7 @@ public JNINativeMethod (string name, string sig, Delegate func) Sig = sig; Func = func; } - } + } #endif // !JAVA_INTEROP #if ANDROID_8 @@ -2081,5 +2039,3 @@ internal static int AndroidBitmap_unlockPixels (IntPtr jbitmap) #endif // ANDROID_8 } } - - diff --git a/src/Mono.Android/Android.Runtime/Logger.cs b/src/Mono.Android/Android.Runtime/Logger.cs index 0a5d18d5430..3bfdea5507b 100644 --- a/src/Mono.Android/Android.Runtime/Logger.cs +++ b/src/Mono.Android/Android.Runtime/Logger.cs @@ -3,9 +3,12 @@ namespace Android.Runtime { internal static class Logger { - internal static LogCategories Categories; + static object initLock = new object (); + static LogCategories logCategories; static bool hasNoLibLog; + internal static LogCategories Categories => logCategories; + internal static bool LogAssembly { get {return (Categories & LogCategories.Assembly) != 0;} } @@ -58,18 +61,28 @@ public static void Log (LogLevel level, string appname, string log) { System.Console.WriteLine ("[{0}] {1}: {2}", level, appname, line); } } + + [DllImport ("__Internal")] + extern static uint monodroid_get_log_categories (); + + static Logger () + { + lock (initLock) { + logCategories = (LogCategories) monodroid_get_log_categories (); + } + } } internal enum LogLevel { - Unknown, - Default, - Verbose, - Debug, - Info, - Warn, - Error, - Fatal, - Silent + Unknown = 0x00, + Default = 0x01, + Verbose = 0x02, + Debug = 0x03, + Info = 0x04, + Warn = 0x05, + Error = 0x06, + Fatal = 0x07, + Silent = 0x08 } // Keep in sync with the LogCategories enum in diff --git a/src/Mono.Android/Java.Interop/TypeManager.cs b/src/Mono.Android/Java.Interop/TypeManager.cs index 49d4108eb13..6d00fe7a808 100644 --- a/src/Mono.Android/Java.Interop/TypeManager.cs +++ b/src/Mono.Android/Java.Interop/TypeManager.cs @@ -12,7 +12,7 @@ namespace Java.Interop { public static partial class TypeManager { // Make this internal so that JNIEnv.Initialize can trigger the static - // constructor so that JNIEnv.RegisterJNINatives() doesn't include + // constructor so that JNIEnv.RegisterJNINatives() doesn't include // the static constructor execution. // Lock on jniToManaged before accessing EITHER jniToManaged or managedToJni. @@ -21,26 +21,6 @@ public static partial class TypeManager { internal static IntPtr id_Class_getName; - static TypeManager () - { - var start = new DateTime (); - if (Logger.LogTiming) { - start = DateTime.UtcNow; - Logger.Log (LogLevel.Info, - "monodroid-timing", - "TypeManager.cctor start: " + (start - new DateTime (1970, 1, 1)).TotalMilliseconds); - } - - __TypeRegistrations.RegisterPackages (); - - if (Logger.LogTiming) { - var end = DateTime.UtcNow; - Logger.Log (LogLevel.Info, - "monodroid-timing", - "TypeManager.cctor time: " + (end - new DateTime (1970, 1, 1)).TotalMilliseconds + " [elapsed: " + (end - start).TotalMilliseconds + " ms]"); - } - } - internal static string GetClassName (IntPtr class_ptr) { return JNIEnv.GetString (JNIEnv.CallObjectMethod (class_ptr, JNIEnv.mid_Class_getName), JniHandleOwnership.TransferLocalRef).Replace (".", "/"); @@ -213,6 +193,8 @@ internal static Type GetJavaToManagedType (string class_name) return null; } + __TypeRegistrations.RegisterPackages (); + var type = (Type) null; int ls = class_name.LastIndexOf ('/'); var package = ls >= 0 ? class_name.Substring (0, ls) : ""; @@ -256,7 +238,7 @@ internal static IJavaObject CreateInstance (IntPtr handle, JniHandleOwnership tr class_name = GetClassName (class_ptr); } } - + JNIEnv.DeleteLocalRef (class_ptr); if (type == null) { @@ -269,7 +251,7 @@ internal static IJavaObject CreateInstance (IntPtr handle, JniHandleOwnership tr if (targetType != null && !targetType.IsAssignableFrom (type)) type = targetType; - + if (type.IsInterface || type.IsAbstract) { var invokerType = JavaObjectExtensions.GetHelperType (type, "Invoker"); if (invokerType == null) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs index 3e6873a4b5b..396c39975ad 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2011 Xamarin, Inc. All rights reserved. +// Copyright (C) 2011 Xamarin, Inc. All rights reserved. using System; using System.Collections.Generic; @@ -9,10 +9,13 @@ using Microsoft.Build.Framework; using Microsoft.Build.Utilities; +using Java.Interop.Tools.TypeNameMappings; using Xamarin.Android.Tools; namespace Xamarin.Android.Tasks { + using PackageNamingPolicyEnum = PackageNamingPolicy; + public class GeneratePackageManagerJava : AndroidTask { public override string TaskPrefix => "GPM"; @@ -55,6 +58,7 @@ public class GeneratePackageManagerJava : AndroidTask [Required] public bool EnablePreloadAssembliesDefault { get; set; } + public string PackageNamingPolicy { get; set; } public string Debug { get; set; } public ITaskItem[] Environments { get; set; } public string AndroidAotMode { get; set; } @@ -83,7 +87,7 @@ public override bool RunTask () int minApiVersion = doc.MinSdkVersion == null ? 4 : (int) doc.MinSdkVersion; // We need to include any special assemblies in the Assemblies list var assemblies = ResolvedUserAssemblies - .Concat (MonoAndroidHelper.GetFrameworkAssembliesToTreatAsUserAssemblies (ResolvedAssemblies)) + .Concat (MonoAndroidHelper.GetFrameworkAssembliesToTreatAsUserAssemblies (ResolvedAssemblies)) .ToList (); var mainFileName = Path.GetFileName (MainAssembly); Func fileNameEq = (a,b) => a.Equals (b, StringComparison.OrdinalIgnoreCase); @@ -141,11 +145,16 @@ void AddEnvironment () { bool usesMonoAOT = false; bool usesAssemblyPreload = EnablePreloadAssembliesDefault; + bool brokenExceptionTransitions = false; uint monoAOTMode = 0; string androidPackageName = null; var environmentVariables = new Dictionary (StringComparer.Ordinal); var systemProperties = new Dictionary (StringComparer.Ordinal); + if (!Enum.TryParse (PackageNamingPolicy, out PackageNamingPolicy pnp)) { + pnp = PackageNamingPolicyEnum.LowercaseHash; + } + AotMode aotMode; if (AndroidAotMode != null && Aot.GetAndroidAotMode (AndroidAotMode, out aotMode)) { usesMonoAOT = true; @@ -189,6 +198,10 @@ void AddEnvironment () } continue; } + if (lineToWrite.StartsWith ("XA_BROKEN_EXCEPTION_TRANSITIONS=")) { + brokenExceptionTransitions = true; + continue; + } AddEnvironmentVariableLine (lineToWrite); } @@ -260,6 +273,8 @@ void AddEnvironment () UsesAssemblyPreload = usesAssemblyPreload, MonoAOTMode = monoAOTMode.ToString ().ToLowerInvariant (), AndroidPackageName = AndroidPackageName, + BrokenExceptionTransitions = brokenExceptionTransitions, + PackageNamingPolicy = pnp, }; using (var sw = new StreamWriter (ms, utf8Encoding, bufferSize: 8192, leaveOpen: true)) { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs index d33e15f2103..eea7e797f45 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; +using Java.Interop.Tools.TypeNameMappings; + namespace Xamarin.Android.Tasks { class ApplicationConfigNativeAssemblyGenerator : NativeAssemblyGenerator @@ -16,6 +18,8 @@ class ApplicationConfigNativeAssemblyGenerator : NativeAssemblyGenerator public bool UsesAssemblyPreload { get; set; } public string MonoAOTMode { get; set; } public string AndroidPackageName { get; set; } + public bool BrokenExceptionTransitions { get; set; } + public PackageNamingPolicy PackageNamingPolicy { get; set; } public ApplicationConfigNativeAssemblyGenerator (NativeAssemblerTargetProvider targetProvider, IDictionary environmentVariables, IDictionary systemProperties) : base (targetProvider) @@ -53,6 +57,12 @@ protected override void WriteSymbols (StreamWriter output) WriteCommentLine (output, "is_a_bundled_app"); size += WriteData (output, IsBundledApp); + WriteCommentLine (output, "broken_exception_transitions"); + size += WriteData (output, BrokenExceptionTransitions); + + WriteCommentLine (output, "package_naming_policy"); + size += WriteData (output, (uint)PackageNamingPolicy); + WriteCommentLine (output, "environment_variable_count"); size += WriteData (output, environmentVariables == null ? 0 : environmentVariables.Count * 2); diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index dc286526c69..ed342f83678 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1,4 +1,4 @@ - - @@ -236,7 +236,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. >False True - True + True False False @@ -245,7 +245,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. - + armeabi-v7a;arm64-v8a @@ -257,7 +257,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. true - + True False @@ -322,11 +322,11 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. {intermediate.application}; @(ProguardConfiguration); - + <_AndroidMainDexListFile>$(IntermediateOutputPath)multidex.keep - + - + <_PackagedResources>$(IntermediateOutputPath)android\bin\packaged_resources <_Android32bitArchitectures>armeabi-v7a;x86;mips @@ -475,7 +475,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. false - + true @@ -493,7 +493,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. - + - + _SetupDesignTimeBuildForCompile; @@ -878,7 +878,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. AotAssemblies="$(AotAssemblies)" Aapt2ToolPath="$(Aapt2ToolPath)" SequencePointsMode="$(_AndroidSequencePointsMode)" - ProjectFilePath="$(MSBuildProjectFullPath)" + ProjectFilePath="$(MSBuildProjectFullPath)" LintToolPath="$(LintToolPath)" ZipAlignPath="$(ZipAlignToolPath)"> @@ -980,7 +980,7 @@ because xbuild doesn't support framework reference assemblies. - + @@ -1130,7 +1130,7 @@ because xbuild doesn't support framework reference assemblies. - + @@ -1198,7 +1198,7 @@ because xbuild doesn't support framework reference assemblies. @@ -1358,14 +1358,14 @@ because xbuild doesn't support framework reference assemblies. - + + DependsOnTargets="$(CoreResolveReferencesDependsOn);_CreatePropertiesCache;_CheckForDeletedResourceFile;_ComputeAndroidResourcePaths;_UpdateAndroidResgen;_CreateManagedLibraryResourceArchive"> - @@ -1375,14 +1375,14 @@ because xbuild doesn't support framework reference assemblies. - + - + @@ -1463,7 +1463,7 @@ because xbuild doesn't support framework reference assemblies. - + @@ -1519,15 +1519,15 @@ because xbuild doesn't support framework reference assemblies. - + - + @@ -1615,18 +1615,18 @@ because xbuild doesn't support framework reference assemblies. - + - + - + @@ -1674,7 +1674,7 @@ because xbuild doesn't support framework reference assemblies. LibraryTextFiles="@(_AdditonalAndroidResourceCachePaths->'%(Identity)\..\R.txt');@(LibraryResourceDirectories->'%(Identity)\..\R.txt')" ManifestFiles="@(_AdditonalAndroidResourceCachePaths->'%(Identity)\AndroidManifest.xml');@(LibraryResourceDirectories->'%(Identity)\..\AndroidManifest.xml')" /> - + - + <_ObsoleteAssemblies>@(ResolvedFrameworkAssemblies) @@ -1908,13 +1908,13 @@ because xbuild doesn't support framework reference assemblies. OutputPath="$(_AndroidIntermediateJavaSourceDirectory)mono\MonoRuntimeProvider.java" Condition=" '$(AndroidUseSharedRuntime)' == 'True' And '$(_AndroidApiLevel)' < '21' " /> - + - + @@ -2024,7 +2024,7 @@ because xbuild doesn't support framework reference assemblies. - + - + - - + - + <_Assemblies>@(_ResolvedFrameworkAssemblies) @@ -2863,7 +2864,7 @@ because xbuild doesn't support framework reference assemblies. ToolPath="$(_MonoAndroidToolsDirectory)" ApkOutputPath="$(_BuildApkEmbedOutputs)" ResolvedAssemblies="@(_ResolvedAssemblies)"> - + - + @@ -3061,7 +3062,7 @@ because xbuild doesn't support framework reference assemblies. - + @@ -3217,7 +3218,7 @@ because xbuild doesn't support framework reference assemblies. @@ -3466,11 +3467,11 @@ because xbuild doesn't support framework reference assemblies. - diff --git a/src/monodroid/jni/application_dso_stub.cc b/src/monodroid/jni/application_dso_stub.cc index 94f91504295..45429b2dfc3 100644 --- a/src/monodroid/jni/application_dso_stub.cc +++ b/src/monodroid/jni/application_dso_stub.cc @@ -17,6 +17,8 @@ ApplicationConfig application_config = { .uses_mono_aot = false, .uses_assembly_preload = false, .is_a_bundled_app = false, + .broken_exception_transitions = false, + .package_naming_policy = 0, .environment_variable_count = 0, .system_property_count = 0, .android_package_name = "com.xamarin.test", diff --git a/src/monodroid/jni/globals.cc b/src/monodroid/jni/globals.cc index ab4238cb016..3e7337003ca 100644 --- a/src/monodroid/jni/globals.cc +++ b/src/monodroid/jni/globals.cc @@ -7,6 +7,7 @@ Util utils; AndroidSystem androidSystem; OSBridge osBridge; EmbeddedAssemblies embeddedAssemblies; +Timing *timing = nullptr; #ifndef ANDROID InMemoryAssemblies inMemoryAssemblies; #endif diff --git a/src/monodroid/jni/globals.hh b/src/monodroid/jni/globals.hh index 04a15a97565..1bd879ae893 100644 --- a/src/monodroid/jni/globals.hh +++ b/src/monodroid/jni/globals.hh @@ -3,6 +3,7 @@ #define __GLOBALS_H #include "util.hh" +#include "timing.hh" #include "debug.hh" #include "embedded-assemblies.hh" #include "inmemory-assemblies.hh" @@ -13,6 +14,8 @@ extern xamarin::android::Util utils; extern xamarin::android::internal::AndroidSystem androidSystem; extern xamarin::android::internal::OSBridge osBridge; extern xamarin::android::internal::EmbeddedAssemblies embeddedAssemblies; +extern xamarin::android::Timing *timing; + #ifndef ANDROID extern xamarin::android::internal::InMemoryAssemblies inMemoryAssemblies; #endif diff --git a/src/monodroid/jni/logger.cc b/src/monodroid/jni/logger.cc index cb1129bdb67..f26a05cee09 100644 --- a/src/monodroid/jni/logger.cc +++ b/src/monodroid/jni/logger.cc @@ -41,6 +41,20 @@ static const char* log_names[] = { "*error*", }; +// Keep in sync with LogLevel defined in JNIEnv.cs +enum class LogLevel : unsigned int +{ + Unknown = 0x00, + Default = 0x01, + Verbose = 0x02, + Debug = 0x03, + Info = 0x04, + Warn = 0x05, + Error = 0x06, + Fatal = 0x07, + Silent = 0x08 +}; + #if defined(__i386__) && defined(__GNUC__) #define ffs(__value__) __builtin_ffs ((__value__)) #elif defined(__x86_64__) && defined(__GNUC__) @@ -236,3 +250,49 @@ log_debug_nocheck (LogCategories category, const char *format, ...) DO_LOG (ANDROID_LOG_DEBUG, category, format, args); } + +// +// DO NOT REMOVE: used by Android.Runtime.Logger +// +MONO_API unsigned int +monodroid_get_log_categories () +{ + return log_categories; +} + +// +// DO NOT REMOVE: used by Android.Runtime.JNIEnv +// +MONO_API void +monodroid_log (LogLevel level, LogCategories category, const char *message) +{ + switch (level) { + case LogLevel::Verbose: + case LogLevel::Debug: + log_debug_nocheck (category, message); + break; + + case LogLevel::Info: + log_info_nocheck (category, message); + break; + + case LogLevel::Warn: + case LogLevel::Silent: // warn is always printed + log_warn (category, message); + break; + + case LogLevel::Error: + log_error (category, message); + break; + + case LogLevel::Fatal: + log_fatal (category, message); + break; + + default: + case LogLevel::Unknown: + case LogLevel::Default: + log_info_nocheck (category, message); + break; + } +} diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index 2cd88a4db07..d2cc788a769 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -71,6 +71,7 @@ #include "monodroid-glue-internal.hh" #include "globals.hh" #include "xamarin-app.h" +#include "timing.hh" #ifndef WINDOWS #include "xamarin_getifaddrs.h" @@ -1101,7 +1102,9 @@ struct JnienvInitializeArgs { int localRefsAreIndirect; int grefGcThreshold; jobject grefIGCUserPeer; - int isRunningOnDesktop; + uint8_t isRunningOnDesktop; + uint8_t brokenExceptionTransitions; + int packageNamingPolicy; }; #define DEFAULT_X_DPI 120.0f @@ -1189,7 +1192,9 @@ init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass runtimeClass, jobj init.version = env->GetVersion (); init.androidSdkVersion = android_api_level; init.localRefsAreIndirect = LocalRefsAreIndirect (env, runtimeClass, init.androidSdkVersion); - init.isRunningOnDesktop = is_running_on_desktop; + init.isRunningOnDesktop = is_running_on_desktop ? 1 : 0; + init.brokenExceptionTransitions = application_config.broken_exception_transitions ? 1 : 0; + init.packageNamingPolicy = application_config.package_naming_policy; // GC threshold is 90% of the max GREF count init.grefGcThreshold = static_cast(androidSystem.get_gref_gc_threshold ()); @@ -1252,9 +1257,7 @@ init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass runtimeClass, jobj if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { partial_time.mark_end (); - - timing_diff diff (partial_time); - log_info_nocheck (LOG_TIMING, "Runtime.init: end native-to-managed transition; elapsed: %lis:%lu::%lu", diff.sec, diff.ms, diff.ns); + Timing::info (partial_time, "Runtime.init: end native-to-managed transition"); } } @@ -1849,9 +1852,7 @@ load_assembly (MonoDomain *domain, JNIEnv *env, jstring_wrapper &assembly) if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { total_time.mark_end (); - - timing_diff diff (total_time); - log_info (LOG_TIMING, "Assembly load: %s preloaded; elapsed: %lis:%lu::%lu", assm_name, diff.sec, diff.ms, diff.ns); + TIMING_LOG_INFO (total_time, "Assembly load: %s preloaded", assm_name); } } @@ -1871,8 +1872,7 @@ load_assemblies (MonoDomain *domain, JNIEnv *env, jstring_array_wrapper &assembl if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { total_time.mark_end (); - timing_diff diff (total_time); - log_info (LOG_TIMING, "Finished loading assemblies: preloaded %u assemblies; wasted time: %lis:%lu::%lu", assemblies.get_length (), diff.sec, diff.ms, diff.ns); + TIMING_LOG_INFO (total_time, "Finished loading assemblies: preloaded %u assemblies", assemblies.get_length ()); } } @@ -1930,6 +1930,7 @@ Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, timing_period total_time; if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { + timing = new Timing (); total_time.mark_start (); } @@ -2069,8 +2070,7 @@ Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { partial_time.mark_end (); - timing_diff diff (partial_time); - log_info_nocheck (LOG_TIMING, "Runtime.init: Mono runtime init; elapsed: %lis:%lu::%lu", diff.sec, diff.ms, diff.ns); + Timing::info (partial_time, "Runtime.init: Mono runtime init"); } jstring_array_wrapper assemblies (env, assembliesJava); @@ -2087,9 +2087,7 @@ Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { total_time.mark_end (); - - timing_diff diff (total_time); - log_info_nocheck (LOG_TIMING, "Runtime.init: end, total time; elapsed: %lis:%lu::%lu", diff.sec, diff.ms, diff.ns); + Timing::info (total_time, "Runtime.init: end, total time"); _monodroid_counters_dump ("## Runtime.init: end"); } } @@ -2136,13 +2134,13 @@ JNICALL Java_mono_android_Runtime_register (JNIEnv *env, jclass klass, jstring m if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { total_time.mark_end (); - timing_diff diff (total_time); - mt_ptr = env->GetStringUTFChars (managedType, nullptr); char *type = utils.strdup_new (mt_ptr); env->ReleaseStringUTFChars (managedType, mt_ptr); - log_info_nocheck (LOG_TIMING, "Runtime.register: end time; elapsed: %lis:%lu::%lu", diff.sec, diff.ms, diff.ns); + log_info_nocheck (LOG_TIMING, "Runtime.register: registered type '%s'", type); + Timing::info (total_time, "Runtime.register: end time"); + _monodroid_counters_dump ("## Runtime.register: type=%s\n", type); delete[] type; } @@ -2279,3 +2277,31 @@ extern "C" void* monodroid_get_dylib (void) { return nullptr; } + +MONO_API managed_timing_sequence* +monodroid_timing_start (const char *message) +{ + if (timing == nullptr) + return nullptr; + + managed_timing_sequence *ret = timing->get_available_sequence (); + if (message != nullptr) { + log_info (LOG_TIMING, message); + } + ret->period.mark_start (); + + return ret; +} + +MONO_API void +monodroid_timing_stop (managed_timing_sequence *sequence, const char *message) +{ + static constexpr const char DEFAULT_MESSAGE[] = "Managed Timing"; + + if (sequence == nullptr) + return; + + sequence->period.mark_end (); + Timing::info (sequence->period, message == nullptr ? DEFAULT_MESSAGE : message); + timing->release_sequence (sequence); +} diff --git a/src/monodroid/jni/timing.hh b/src/monodroid/jni/timing.hh new file mode 100644 index 00000000000..18adfc66084 --- /dev/null +++ b/src/monodroid/jni/timing.hh @@ -0,0 +1,161 @@ +#ifndef __TIMING_HH +#define __TIMING_HH + +#include +#include +#include + +#ifdef ANDROID +#include +#endif + +#include "cppcompat.hh" +#include "logger.hh" + +namespace xamarin::android +{ + struct timing_point + { + time_t sec; + uint64_t ns; + + void mark (); + }; + + struct timing_period + { + timing_point start; + timing_point end; + + void mark_start () + { + start.mark (); + } + + void mark_end () + { + end.mark (); + } + }; + + struct timing_diff + { + static constexpr uint32_t ms_in_nsec = 1000000ULL; + + time_t sec; + uint32_t ms; + uint32_t ns; + + timing_diff (const timing_period &period); + }; + + struct managed_timing_sequence + { + timing_period period; + bool in_use; + bool dynamic; + }; + + // This class is intended to be used by the managed code. It can be used by the native code as + // well, but the overhead it has (out of necessity) might not be desirable in native code. +#define TIMING_FORMAT "; elapsed: %lis:%lu::%lu" + + class Timing + { + static constexpr char MESSAGE_FORMAT[] = "%s" TIMING_FORMAT; + + public: + static constexpr size_t DEFAULT_POOL_SIZE = 16; + + public: + explicit Timing (size_t initial_pool_size = DEFAULT_POOL_SIZE) noexcept + : sequence_pool_size (initial_pool_size) + { + sequence_pool = new managed_timing_sequence [initial_pool_size] (); + } + + ~Timing () noexcept + { + delete[] sequence_pool; + } + + static void debug (timing_period const &period, const char *message) noexcept + { + timing_diff diff (period); + + log_debug_nocheck (LOG_TIMING, MESSAGE_FORMAT, message == nullptr ? "" : message, diff.sec, diff.ms, diff.ns); + } + + static void info (timing_period const &period, const char *message) noexcept + { + timing_diff diff (period); + + log_info_nocheck (LOG_TIMING, MESSAGE_FORMAT, message == nullptr ? "" : message, diff.sec, diff.ms, diff.ns); + } + + static void warn (timing_period const &period, const char *message) noexcept + { + timing_diff diff (period); + + log_warn (LOG_TIMING, MESSAGE_FORMAT, message == nullptr ? "" : message, diff.sec, diff.ms, diff.ns); + } + + managed_timing_sequence* get_available_sequence () noexcept + { + std::lock_guard lock (sequence_lock); + + managed_timing_sequence *ret; + for (size_t i = 0; i < sequence_pool_size; i++) { + if (sequence_pool[i].in_use) { + continue; + } + + ret = &sequence_pool[i]; + ret->in_use = true; + ret->dynamic = false; + + return ret; + } + + ret = new managed_timing_sequence (); + ret->dynamic = true; + + return ret; + } + + void release_sequence (managed_timing_sequence *sequence) + { + if (sequence == nullptr) + return; + + std::lock_guard lock (sequence_lock); + if (sequence->dynamic) { + delete sequence; + return; + } + + sequence->in_use = false; + } + + private: + managed_timing_sequence *sequence_pool; + size_t sequence_pool_size; + std::mutex sequence_lock; + }; + + // This is a hack to avoid having to allocate memory when rendering messages that use additional + // format placeholders on the caller side. Memory allocation would be necessary since we append + // the standard timing suffix to every message printed. Using a variadic macro allows us to + // compose a call with all the elements present and make the composition compile-time. + // + // It could be done with template packs but that would result in extra code generated whenever a + // call with a different set of parameters would be made, plus the code to implement that would + // be a bit verbose and unwieldy, so we will stick to this simple method. +#define TIMING_DO_LOG(_level, _category_, ...) ::log_ ## _level ## _nocheck ((_category_), __VA_ARGS__) + +#define TIMING_LOG_INFO(__period__, __format__, ...) { \ + timing_diff diff ((__period__)); \ + TIMING_DO_LOG (info, LOG_TIMING, __format__ TIMING_FORMAT, __VA_ARGS__, diff.sec, diff.ms, diff.ns); \ + } +} +#endif // __TIMING_HH diff --git a/src/monodroid/jni/util.hh b/src/monodroid/jni/util.hh index a43924dca2b..54672f8f92d 100644 --- a/src/monodroid/jni/util.hh +++ b/src/monodroid/jni/util.hh @@ -62,41 +62,6 @@ extern "C" { namespace xamarin::android { - struct timing_point - { - time_t sec; - uint64_t ns; - - void mark (); - }; - - struct timing_period - { - timing_point start; - timing_point end; - - void mark_start () - { - start.mark (); - } - - void mark_end () - { - end.mark (); - } - }; - - struct timing_diff - { - static constexpr uint32_t ms_in_nsec = 1000000ULL; - - time_t sec; - uint32_t ms; - uint32_t ns; - - timing_diff (const timing_period &period); - }; - class Util : public BasicUtilities { #if defined (ANDROID) || defined (LINUX) diff --git a/src/monodroid/jni/xamarin-app.h b/src/monodroid/jni/xamarin-app.h index b771b7b7506..1de8a893b5e 100644 --- a/src/monodroid/jni/xamarin-app.h +++ b/src/monodroid/jni/xamarin-app.h @@ -20,6 +20,8 @@ struct ApplicationConfig bool uses_mono_aot; bool uses_assembly_preload; bool is_a_bundled_app; + bool broken_exception_transitions; + uint32_t package_naming_policy; uint32_t environment_variable_count; uint32_t system_property_count; const char *android_package_name;