From b90d3ab717cbfd38b2e6a307b4387d2d6cf04af8 Mon Sep 17 00:00:00 2001 From: Marek Habersack Date: Fri, 4 Jan 2019 21:50:16 +0100 Subject: [PATCH] [monodroid] Runtime startup performance improvements (#2515) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://github.com/xamarin/xamarin-android/issues/1443 Context: https://jenkins.mono-project.com/view/Xamarin.Android/job/xamarin-android/plot/Tests%20times/ A frequent complaint with Xamarin.Android is that application startup times can be slower than desired. For example, see the [Xamarin.Forms app startup data][xf-startup], in particular the **init-Release** column (times in milliseconds), which is the time at which the `Runtime.init: end native-to-managed transition` message is printed when `timing` messages are enabled. As of Jenkins build 1359, the **init-Release** value is 518ms. (Note that the times in that table are not necessarily useful, nor are they future proof: they come from the emulator that the Jenkins build machine uses, and that emulator *has* changed and *will* change in the future. Additionally, because it's an x86-accelerated emulator, times are not meaningful to Android hardware devices. The values also fluctuate a lot. Better than nothing? ¯\_(ツ)_/¯) Optimize some startup operations to reduce the **init-Release** value: * JNI Handling Improvements: * Reduce Java class lookup. * Don't lookup `android.os.Build.VERSION.SDK_INT` via JNI. * `jstring` handling improvements. * Review logging messages. * Improve package name hash generation. * Improve environment variable processing. * Mono Handling Improvements * Stop preloading all assemblies. * Avoid using "system properties" to control Mono features. * Desktop version is now a compile-time build option, not runtime. * Initialize `xamarin_getifaddrs()` on-demand, not at startup. [xf-startup]: https://jenkins.mono-project.com/view/Xamarin.Android/job/xamarin-android/plot/Tests%20times/#plot-8694435842335223270.csv ~~ JNI Handling Improvements ~~ Previously, JNI was used to lookup the `android.os.Build.VERSION.SDK_INT` value. We now pass `Build.VERSION.SDK_INT` into `Runtime.init()`, avoiding the JNI calls. Previously, `JNIEnv::FindClass()` was used during process startup in order to lookup multiple types which are required for operation. These types are now fields on the `mono.android.Runtime` type, and `JNIEnv::GetStaticObjectField()` is used to obtain the `jclass`. Additionally, a handful of class field/method lookups were moved out of the init code so that the code that doesn't use them doesn't have to pay the tax. `jstring` and `jobjectArray`-of-`jstring` values are now handled by the new `jstring_wrapper` and `jstring_array_wrapper` types, which take care of efficiently caching the retrieved strings as well as of correctly deleting local references to the obtained objects. Both classes are optimized so that they compile into the equivalent of the current, hand-written, code. They also take care to make the minimum necessary number of calls in order to access the strings, both standalone and from arrays, as well as to release the resources. The string and array wrapper instances are passed around as references, thus using the minimum amount of memory. ~~ Log Messages ~~ Previously whenever any of the `log_{info,debug}()` functions were called we'd spend time preparing all the parameters to pass to the function, sometimes involving memory allocation, function calls, etc., only to discard all of that work **inside** the `log_*` call because the logging category used in that call was disabled. Now we check whether the category is enabled before we set out to construct the parameters. ~~ Environment Processing ~~ Since [2012-Aug-16][env-support], `@(AndroidEnvironment)` has worked by creating a file named `environment` within the `.apk` -- which is stored uncompressed within the `.apk` -- and the file is then processed, calling **setenv**(3) to store the recorded values. There's a fair bit of potentially hairy string manipulation here, from ***C***, which is not entirely ideal or performant. To speed the process up, this commit replaces the `environment` file with a Java class generated during application build which contains an array of `"name", "value"` pairs. The class is passed to `Java_mono_android_Runtime_init()` and its elements are used to create the requested environment variables. Some of these variables are special-cased; instead of using them for **setenv**(3), they control flags in the `AndroidSystem` class * `mono.aot`: The `$(AndroidAotMode)` value; which *kind* of AOT mono should support at runtime. * `mono.llvm`: The `$(EnableLLVM)` value; whether LLVM-generated AOT files should be used. * `__XA_DSO_IN_APK`: Support in-place reading of `.so` files; See commit 95ca1025. [env-support]: https://github.com/xamarin/monodroid/commit/dbd73ec8343477c8fd10379c6577a38c072a78fa ~~ Mono Handling Improvements ~~ During process startup startup, *every* assembly within the `.apk` would be loaded so that a `Java.Interop.__TypeRegistrations` type could be probed, and if it existed, the `__TypeRegistrations.RegisterPackages()` method would be invoked. This was done in order to [better support "type mappings"][typemaps] between Java names and C# names (and vice versa). However, this support hasn't been required since the introduction of the [`typemap.jm` and `typemap.mj` files][typemap-files]; we just hadn't gotten around to removing the `__TypeRegistrations.RegisterPackages()` invocation. *Not* loading every assembly on startup allows assemblies to be loaded on-demand, and improves startup times. [typemaps]: https://github.com/xamarin/xamarin-android/issues/1443#issuecomment-374343825 [typemap-files]: https://github.com/xamarin/monodroid/commit/e69b76e2b86f92fa8440756c44ad3e2205563ac7 ~~ Startup Time Summary ~~ Startup times for `tests/Xamarin.Forms-Performance-Integration`, average of 50 iterations of (uninstall app, install app, launch app): * Debug configuration: Old: 1s 440ms New: 1s 100ms * Release configuration: Old: 650ms New: 270ms --- build-tools/scripts/cmake-common.props | 2 +- src/Mono.Android/Android.Runtime/JNIEnv.cs | 41 +- .../java/mono/android/Runtime.java | 7 +- .../Resources/MonoPackageManager.java | 4 +- .../XamarinAndroidEnvironmentVariables.java | 9 + .../Tasks/BuildApk.cs | 101 ---- .../Tasks/GeneratePackageManagerJava.cs | 153 +++++- .../Xamarin.Android.Build.Tests/BuildTest.cs | 51 +- .../PackagingTest.cs | 33 +- .../Xamarin.Android.Build.Tasks.csproj | 3 + .../Xamarin.Android.Common.targets | 23 +- src/monodroid/CMakeLists.txt | 4 + src/monodroid/jni/android-system.cc | 148 +++--- src/monodroid/jni/android-system.h | 46 +- src/monodroid/jni/cppcompat.h | 47 ++ src/monodroid/jni/debug.cc | 4 +- src/monodroid/jni/dylib-mono.cc | 12 + src/monodroid/jni/dylib-mono.h | 4 +- src/monodroid/jni/embedded-assemblies.cc | 20 +- src/monodroid/jni/globals.h | 1 + src/monodroid/jni/jni-wrappers.h | 179 +++++++ src/monodroid/jni/mono_android_Runtime.h | 4 +- src/monodroid/jni/monodroid-glue-internal.h | 1 - src/monodroid/jni/monodroid-glue.cc | 479 ++++++------------ src/monodroid/jni/monodroid.h | 8 +- src/monodroid/jni/new_delete.cc | 79 +++ src/monodroid/jni/osbridge.cc | 14 +- src/monodroid/jni/osbridge.h | 2 +- src/monodroid/jni/util.cc | 86 +++- src/monodroid/jni/util.h | 28 +- src/monodroid/jni/xamarin_getifaddrs.cc | 76 ++- src/monodroid/monodroid.targets | 8 +- .../EmbeddedDSO-UnitTests/BuildTests.cs | 48 +- .../Droid/Properties/AndroidManifest.xml | 2 +- ...Forms.Performance.Integration.Droid.csproj | 4 +- 35 files changed, 1053 insertions(+), 678 deletions(-) create mode 100644 src/Xamarin.Android.Build.Tasks/Resources/XamarinAndroidEnvironmentVariables.java create mode 100644 src/monodroid/jni/jni-wrappers.h create mode 100644 src/monodroid/jni/new_delete.cc diff --git a/build-tools/scripts/cmake-common.props b/build-tools/scripts/cmake-common.props index fa1934051f3..ec00eae9b54 100644 --- a/build-tools/scripts/cmake-common.props +++ b/build-tools/scripts/cmake-common.props @@ -2,6 +2,6 @@ <_CmakeCommonFlags>-GNinja -DCMAKE_MAKE_PROGRAM=$(NinjaPath) - <_CmakeAndroidFlags>$(_CmakeCommonFlags) -DANDROID_TOOLCHAIN=clang -DANDROID_NATIVE_API_LEVEL=$(AndroidNdkApiLevel) -DANDROID_PLATFORM=android-$(AndroidNdkApiLevel) -DCMAKE_TOOLCHAIN_FILE=$(AndroidNdkDirectory)\build\cmake\android.toolchain.cmake -DANDROID_NDK=$(AndroidNdkDirectory) + <_CmakeAndroidFlags>$(_CmakeCommonFlags) -DANDROID_STL="system" -DANDROID_CPP_FEATURES="" -DANDROID_TOOLCHAIN=clang -DANDROID_NATIVE_API_LEVEL=$(AndroidNdkApiLevel) -DANDROID_PLATFORM=android-$(AndroidNdkApiLevel) -DCMAKE_TOOLCHAIN_FILE=$(AndroidNdkDirectory)\build\cmake\android.toolchain.cmake -DANDROID_NDK=$(AndroidNdkDirectory) diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs index 1ca1a11a996..66152a7fd3f 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnv.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs @@ -152,15 +152,16 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) { Logger.Categories = (LogCategories) args->logCategories; - var __start = new DateTime (); + Stopwatch stopper = null; + long elapsed, totalElapsed = 0; if (Logger.LogTiming) { - __start = DateTime.UtcNow; - Logger.Log (LogLevel.Info, - "monodroid-timing", - "JNIEnv.Initialize start: " + (__start - new DateTime (1970, 1, 1)).TotalMilliseconds); - Logger.Log (LogLevel.Info, - "monodroid-timing", - "JNIEnv.Initialize: Logger JIT/etc. time: " + (DateTime.UtcNow - new DateTime (1970, 1, 1)).TotalMilliseconds + " [elapsed: " + (DateTime.UtcNow - __start).TotalMilliseconds + " ms]"); + 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 (); } gref_gc_threshold = args->grefGcThreshold; @@ -195,16 +196,14 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) #endif // JAVA_INTEROP if (Logger.LogTiming) { - var __end = DateTime.UtcNow; - Logger.Log (LogLevel.Info, - "monodroid-timing", - "JNIEnv.Initialize: time: " + (__end - new DateTime (1970, 1, 1)).TotalMilliseconds + " [elapsed: " + (__end - __start).TotalMilliseconds + " ms]"); - __start = DateTime.UtcNow; + elapsed = stopper.ElapsedMilliseconds; + totalElapsed += elapsed; + Logger.Log (LogLevel.Info, "monodroid-timing", $"JNIEnv.Initialize: managed runtime init time: elapsed {elapsed} ms]"); + stopper.Restart (); var _ = Java.Interop.TypeManager.jniToManaged; - __end = DateTime.UtcNow; - Logger.Log (LogLevel.Info, - "monodroid-timing", - "JNIEnv.Initialize: TypeManager init time: " + (__end - new DateTime (1970, 1, 1)).TotalMilliseconds + " [elapsed: " + (__end - __start).TotalMilliseconds + " ms]"); + elapsed = stopper.ElapsedMilliseconds; + totalElapsed += elapsed; + Logger.Log (LogLevel.Info, "monodroid-timing", $"JNIEnv.Initialize: TypeManager init time: elapsed {elapsed} ms]"); } AllocObjectSupported = androidSdkVersion > 10; @@ -238,10 +237,10 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) Java.Lang.Thread.DefaultUncaughtExceptionHandler = defaultUncaughtExceptionHandler; } - if (Logger.LogTiming) - Logger.Log (LogLevel.Info, - "monodroid-timing", - "JNIEnv.Initialize end: " + (DateTime.UtcNow - new DateTime (1970, 1, 1)).TotalMilliseconds); + if (Logger.LogTiming) { + totalElapsed += stopper.ElapsedMilliseconds; + Logger.Log (LogLevel.Info, "monodroid-timing", $"JNIEnv.Initialize end: elapsed {totalElapsed} ms"); + } } internal static void Exit () diff --git a/src/Mono.Android/java/mono/android/Runtime.java b/src/Mono.Android/java/mono/android/Runtime.java index 16fcd441cf1..921551a9738 100644 --- a/src/Mono.Android/java/mono/android/Runtime.java +++ b/src/Mono.Android/java/mono/android/Runtime.java @@ -1,12 +1,17 @@ package mono.android; public class Runtime { + static java.lang.Class java_lang_Class = java.lang.Class.class;; + static java.lang.Class java_lang_System = java.lang.System.class; + static java.lang.Class java_util_TimeZone = java.util.TimeZone.class; + static java.lang.Class mono_android_IGCUserPeer = mono.android.IGCUserPeer.class; + static java.lang.Class mono_android_GCUserPeer = mono.android.GCUserPeer.class; private Runtime () { } - public static native void init (String lang, String[] runtimeApks, String runtimeDataDir, String[] appDirs, ClassLoader loader, String[] externalStorageDirs, String[] assemblies, String packageName); + public static native void init (String lang, String[] runtimeApks, String runtimeDataDir, String[] appDirs, ClassLoader loader, String[] externalStorageDirs, String[] assemblies, String packageName, int apiLevel, String[] environmentVariables); public static native void register (String managedType, java.lang.Class nativeClass, String methods); public static native void notifyTimeZoneChanged (); public static native int createNewContext (String[] runtimeApks, String[] assemblies, ClassLoader loader); diff --git a/src/Xamarin.Android.Build.Tasks/Resources/MonoPackageManager.java b/src/Xamarin.Android.Build.Tasks/Resources/MonoPackageManager.java index 8a1381044ee..317cfdbc2b5 100644 --- a/src/Xamarin.Android.Build.Tasks/Resources/MonoPackageManager.java +++ b/src/Xamarin.Android.Build.Tasks/Resources/MonoPackageManager.java @@ -63,7 +63,9 @@ public static void LoadApplication (Context context, ApplicationInfo runtimePack externalLegacyDir }, MonoPackageManager_Resources.Assemblies, - context.getPackageName ()); + context.getPackageName (), + android.os.Build.VERSION.SDK_INT, + mono.android.app.XamarinAndroidEnvironmentVariables.Variables); mono.android.app.ApplicationRegistration.registerApplications (); diff --git a/src/Xamarin.Android.Build.Tasks/Resources/XamarinAndroidEnvironmentVariables.java b/src/Xamarin.Android.Build.Tasks/Resources/XamarinAndroidEnvironmentVariables.java new file mode 100644 index 00000000000..00d41fcdd6d --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Resources/XamarinAndroidEnvironmentVariables.java @@ -0,0 +1,9 @@ +package mono.android.app; + +public class XamarinAndroidEnvironmentVariables +{ + // Variables are specified the in "name", "value" pairs + public static final String[] Variables = new String[] { +//@ENVVARS@ + }; +} diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs index 3162f098c9a..27d45b83b14 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/BuildApk.cs @@ -45,8 +45,6 @@ public class BuildApk : Task public ITaskItem[] BundleNativeLibraries { get; set; } - public ITaskItem[] Environments { get; set; } - public ITaskItem[] TypeMappings { get; set; } [Required] @@ -74,28 +72,17 @@ public class BuildApk : Task public bool PreferNativeLibrariesWithDebugSymbols { get; set; } - public string AndroidAotMode { get; set; } - public string AndroidSequencePointsMode { get; set; } - public bool EnableLLVM { get; set; } - - public bool EnableSGenConcurrent { get; set; } - public string AndroidEmbedProfilers { get; set; } - public string HttpClientHandlerType { get; set; } public string TlsProvider { get; set; } public string UncompressedFileExtensions { get; set; } - static readonly string MSBuildXamarinAndroidDirectory = Path.GetDirectoryName (typeof (BuildApk).Assembly.Location); [Output] public ITaskItem[] OutputFiles { get; set; } - [Output] - public string BuildId { get; set; } - bool _Debug { get { return string.Equals (Debug, "true", StringComparison.OrdinalIgnoreCase); @@ -103,8 +90,6 @@ bool _Debug { } SequencePointsMode sequencePointsMode = SequencePointsMode.None; - - Guid buildId = Guid.NewGuid (); public ITaskItem[] LibraryProjectJars { get; set; } string [] uncompressedFileExtensions; @@ -125,7 +110,6 @@ void ExecuteWithAbi (string supportedAbis, string apkInputPath, string apkOutput if (EmbedAssemblies && !BundleAssemblies) AddAssemblies (apk); - AddEnvironment (apk); AddRuntimeLibraries (apk, supportedAbis); apk.Flush(); AddNativeLibraries (files, supportedAbis); @@ -206,11 +190,9 @@ public override bool Execute () Log.LogDebugMessage (" Debug: {0}", Debug ?? "no"); Log.LogDebugMessage (" PreferNativeLibrariesWithDebugSymbols: {0}", PreferNativeLibrariesWithDebugSymbols); Log.LogDebugMessage (" EmbedAssemblies: {0}", EmbedAssemblies); - Log.LogDebugMessage (" AndroidAotMode: {0}", AndroidAotMode); Log.LogDebugMessage (" AndroidSequencePointsMode: {0}", AndroidSequencePointsMode); Log.LogDebugMessage (" CreatePackagePerAbi: {0}", CreatePackagePerAbi); Log.LogDebugMessage (" UncompressedFileExtensions: {0}", UncompressedFileExtensions); - Log.LogDebugTaskItems (" Environments:", Environments); Log.LogDebugTaskItems (" ResolvedUserAssemblies:", ResolvedUserAssemblies); Log.LogDebugTaskItems (" ResolvedFrameworkAssemblies:", ResolvedFrameworkAssemblies); Log.LogDebugTaskItems (" NativeLibraries:", NativeLibraries); @@ -220,8 +202,6 @@ public override bool Execute () Log.LogDebugTaskItems (" JavaLibraries:", JavaLibraries); Log.LogDebugTaskItems (" LibraryProjectJars:", LibraryProjectJars); Log.LogDebugTaskItems (" AdditionalNativeLibraryReferences:", AdditionalNativeLibraryReferences); - Log.LogDebugTaskItems (" HttpClientHandlerType:", HttpClientHandlerType); - Log.LogDebugMessage (" TlsProvider: {0}", TlsProvider); Aot.TryGetSequencePointsMode (AndroidSequencePointsMode, out sequencePointsMode); @@ -246,10 +226,6 @@ public override bool Execute () } } - BuildId = buildId.ToString (); - - Log.LogDebugMessage (" [Output] BuildId: {0}", BuildId); - OutputFiles = outputFiles.Select (a => new TaskItem (a)).ToArray (); Log.LogDebugTaskItems (" [Output] OutputFiles :", OutputFiles); @@ -349,83 +325,6 @@ static string GetTargetDirectory (string path) return "assemblies"; } - void AddEnvironment (ZipArchiveEx apk) - { - var environment = new StringWriter () { - NewLine = "\n", - }; - - if (EnableLLVM) { - environment.WriteLine ("mono.llvm=true"); - } - - AotMode aotMode; - if (AndroidAotMode != null && Aot.GetAndroidAotMode(AndroidAotMode, out aotMode)) { - environment.WriteLine ("mono.aot={0}", aotMode.ToString().ToLowerInvariant()); - } - - const string defaultLogLevel = "MONO_LOG_LEVEL=info"; - const string defaultMonoDebug = "MONO_DEBUG=gen-compact-seq-points"; - const string defaultHttpMessageHandler = "XA_HTTP_CLIENT_HANDLER_TYPE=System.Net.Http.HttpClientHandler, System.Net.Http"; - const string defaultTlsProvider = "XA_TLS_PROVIDER=btls"; - string xamarinBuildId = string.Format ("XAMARIN_BUILD_ID={0}", buildId); - - bool haveLogLevel = false; - bool haveMonoDebug = false; - bool havebuildId = false; - bool haveHttpMessageHandler = false; - bool haveTlsProvider = false; - bool haveMonoGCParams = false; - - foreach (ITaskItem env in Environments ?? new TaskItem[0]) { - environment.WriteLine ("## Source File: {0}", env.ItemSpec); - foreach (string line in File.ReadLines (env.ItemSpec)) { - var lineToWrite = line; - if (lineToWrite.StartsWith ("MONO_LOG_LEVEL=", StringComparison.Ordinal)) - haveLogLevel = true; - if (lineToWrite.StartsWith ("MONO_GC_PARAMS=", StringComparison.Ordinal)) - haveMonoGCParams = true; - if (lineToWrite.StartsWith ("XAMARIN_BUILD_ID=", StringComparison.Ordinal)) - havebuildId = true; - if (lineToWrite.StartsWith ("MONO_DEBUG=", StringComparison.Ordinal)) { - haveMonoDebug = true; - if (sequencePointsMode != SequencePointsMode.None && !lineToWrite.Contains ("gen-compact-seq-points")) - lineToWrite = line + ",gen-compact-seq-points"; - } - if (lineToWrite.StartsWith ("XA_HTTP_CLIENT_HANDLER_TYPE=", StringComparison.Ordinal)) - haveHttpMessageHandler = true; - if (lineToWrite.StartsWith ("XA_TLS_PROVIDER=", StringComparison.Ordinal)) - haveTlsProvider = true; - environment.WriteLine (lineToWrite); - } - } - - if (_Debug && !haveLogLevel) { - environment.WriteLine (defaultLogLevel); - } - - if (sequencePointsMode != SequencePointsMode.None && !haveMonoDebug) { - environment.WriteLine (defaultMonoDebug); - } - - if (!havebuildId) - environment.WriteLine (xamarinBuildId); - - if (!haveHttpMessageHandler) - environment.WriteLine (HttpClientHandlerType == null ? defaultHttpMessageHandler : $"XA_HTTP_CLIENT_HANDLER_TYPE={HttpClientHandlerType.Trim ()}"); - if (!haveTlsProvider) - environment.WriteLine (TlsProvider == null ? defaultTlsProvider : $"XA_TLS_PROVIDER={TlsProvider.Trim ()}"); - if (!haveMonoGCParams) { - if (EnableSGenConcurrent) - environment.WriteLine ("MONO_GC_PARAMS=major=marksweep-conc"); - else - environment.WriteLine ("MONO_GC_PARAMS=major=marksweep"); - } - - apk.Archive.AddEntry ("environment", environment.ToString (), - new UTF8Encoding (encoderShouldEmitUTF8Identifier:false)); - } - class LibInfo { public string Path; diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs index 99f9ffab016..370c146457f 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs @@ -13,6 +13,10 @@ namespace Xamarin.Android.Tasks { public class GeneratePackageManagerJava : Task { + const string EnvironmentFileName = "XamarinAndroidEnvironmentVariables.java"; + + Guid buildId = Guid.NewGuid (); + [Required] public ITaskItem[] ResolvedAssemblies { get; set; } @@ -22,6 +26,9 @@ public class GeneratePackageManagerJava : Task [Required] public string OutputDirectory { get; set; } + [Required] + public string EnvironmentOutputDirectory { get; set; } + [Required] public string UseSharedRuntime { get; set; } @@ -34,6 +41,24 @@ public class GeneratePackageManagerJava : Task [Required] public string Manifest { get; set; } + public string Debug { get; set; } + public ITaskItem[] Environments { get; set; } + public string AndroidAotMode { get; set; } + public bool EnableLLVM { get; set; } + public string HttpClientHandlerType { get; set; } + public string TlsProvider { get; set; } + public string AndroidSequencePointsMode { get; set; } + public bool EnableSGenConcurrent { get; set; } + + [Output] + public string BuildId { get; set; } + + bool _Debug { + get { + return string.Equals (Debug, "true", StringComparison.OrdinalIgnoreCase); + } + } + public override bool Execute () { Log.LogDebugMessage ("GeneratePackageManagerJava Task"); @@ -45,6 +70,9 @@ public override bool Execute () Log.LogDebugTaskItems (" ResolvedAssemblies:", ResolvedAssemblies); Log.LogDebugTaskItems (" ResolvedUserAssemblies:", ResolvedUserAssemblies); + BuildId = buildId.ToString (); + Log.LogDebugMessage (" [Output] BuildId: {0}", BuildId); + var shared_runtime = string.Compare (UseSharedRuntime, "true", true) == 0; var doc = AndroidAppManifest.Load (Manifest, MonoAndroidHelper.SupportedVersions); int minApiVersion = doc.MinSdkVersion == null ? 4 : (int) doc.MinSdkVersion; @@ -103,10 +131,131 @@ public override bool Execute () MonoAndroidHelper.CopyIfChanged (temp, dest); try { File.Delete (temp); } catch (Exception) { } - - try { File.Delete (temp); } catch (Exception) { } + + AddEnvironment (); return !Log.HasLoggedErrors; } + + static readonly string[] defaultLogLevel = {"MONO_LOG_LEVEL", "info"}; + static readonly string[] defaultMonoDebug = {"MONO_DEBUG", "gen-compact-seq-points"}; + static readonly string[] defaultHttpMessageHandler = {"XA_HTTP_CLIENT_HANDLER_TYPE", "System.Net.Http.HttpClientHandler, System.Net.Http"}; + static readonly string[] defaultTlsProvider = {"XA_TLS_PROVIDER", "btls"}; + + void AddEnvironment () + { + var environment = new StringWriter () { + NewLine = "\n", + }; + + if (EnableLLVM) { + WriteEnvironment ("mono.llvm", "true"); + } + + AotMode aotMode; + if (AndroidAotMode != null && Aot.GetAndroidAotMode (AndroidAotMode, out aotMode)) { + WriteEnvironment ("mono.aot", aotMode.ToString ().ToLowerInvariant()); + } + + bool haveLogLevel = false; + bool haveMonoDebug = false; + bool havebuildId = false; + bool haveHttpMessageHandler = false; + bool haveTlsProvider = false; + bool haveMonoGCParams = false; + + SequencePointsMode sequencePointsMode; + if (!Aot.TryGetSequencePointsMode (AndroidSequencePointsMode, out sequencePointsMode)) + sequencePointsMode = SequencePointsMode.None; + + foreach (ITaskItem env in Environments ?? new TaskItem[0]) { + environment.WriteLine ("\t\t// Source File: {0}", env.ItemSpec); + foreach (string line in File.ReadLines (env.ItemSpec)) { + var lineToWrite = line; + if (lineToWrite.StartsWith ("MONO_LOG_LEVEL=", StringComparison.Ordinal)) + haveLogLevel = true; + if (lineToWrite.StartsWith ("MONO_GC_PARAMS=", StringComparison.Ordinal)) + haveMonoGCParams = true; + if (lineToWrite.StartsWith ("XAMARIN_BUILD_ID=", StringComparison.Ordinal)) + havebuildId = true; + if (lineToWrite.StartsWith ("MONO_DEBUG=", StringComparison.Ordinal)) { + haveMonoDebug = true; + if (sequencePointsMode != SequencePointsMode.None && !lineToWrite.Contains ("gen-compact-seq-points")) + lineToWrite = line + ",gen-compact-seq-points"; + } + if (lineToWrite.StartsWith ("XA_HTTP_CLIENT_HANDLER_TYPE=", StringComparison.Ordinal)) + haveHttpMessageHandler = true; + if (lineToWrite.StartsWith ("XA_TLS_PROVIDER=", StringComparison.Ordinal)) + haveTlsProvider = true; + WriteEnvironmentLine (lineToWrite); + } + } + + if (_Debug && !haveLogLevel) { + WriteEnvironment (defaultLogLevel[0], defaultLogLevel[1]); + } + + if (sequencePointsMode != SequencePointsMode.None && !haveMonoDebug) { + WriteEnvironment (defaultMonoDebug[0], defaultMonoDebug[1]); + } + + if (!havebuildId) + WriteEnvironment ("XAMARIN_BUILD_ID", buildId.ToString ()); + + if (!haveHttpMessageHandler) { + if (HttpClientHandlerType == null) + WriteEnvironment (defaultHttpMessageHandler[0], defaultHttpMessageHandler[1]); + else + WriteEnvironment ("XA_HTTP_CLIENT_HANDLER_TYPE", HttpClientHandlerType.Trim ()); + } + + if (!haveTlsProvider) { + if (TlsProvider == null) + WriteEnvironment (defaultTlsProvider[0], defaultTlsProvider[1]); + else + WriteEnvironment ("XA_TLS_PROVIDER", TlsProvider.Trim ()); + } + + if (!haveMonoGCParams) { + if (EnableSGenConcurrent) + WriteEnvironment ("MONO_GC_PARAMS", "major=marksweep-conc"); + else + WriteEnvironment ("MONO_GC_PARAMS", "major=marksweep"); + } + + string environmentTemplate; + using (var sr = new StreamReader (typeof (BuildApk).Assembly.GetManifestResourceStream (EnvironmentFileName))) { + environmentTemplate = sr.ReadToEnd (); + } + + using (var ms = new MemoryStream ()) { + using (var sw = new StreamWriter (ms)) { + sw.Write (environmentTemplate.Replace ("//@ENVVARS@", environment.ToString ())); + sw.Flush (); + + string dest = Path.GetFullPath (Path.Combine (EnvironmentOutputDirectory, EnvironmentFileName)); + MonoAndroidHelper.CopyIfStreamChanged (ms, dest); + } + } + + void WriteEnvironment (string name, string value) + { + environment.WriteLine ($"\t\t\"{ValidJavaString (name)}\", \"{ValidJavaString (value)}\","); + } + + void WriteEnvironmentLine (string line) + { + if (String.IsNullOrEmpty (line)) + return; + + string[] nv = line.Split (new char[]{'='}, 2); + WriteEnvironment (nv[0].Trim (), nv.Length < 2 ? String.Empty : nv[1].Trim ()); + } + + string ValidJavaString (string s) + { + return s.Replace ("\"", "\\\""); + } + } } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs index 086b8579cd9..c7a888dafa2 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs @@ -1773,19 +1773,21 @@ public void BuildApplicationWithMonoEnvironment ([Values ("", "Normal", "Offline Assert.IsTrue (libb.Build (lib), "Library build should have succeeded."); Assert.IsTrue (appb.Build (app), "App should have succeeded."); Assert.IsTrue (StringAssertEx.ContainsText (appb.LastBuildOutput, $"Save assembly: {linkSkip}"), $"{linkSkip} should be saved, and not linked!"); - var apk = Path.Combine (Root, appb.ProjectDirectory, - app.IntermediateOutputPath, "android", "bin", "UnnamedProject.UnnamedProject.apk"); - using (var zipFile = ZipHelper.OpenZip (apk)) { - var data = ZipHelper.ReadFileFromZip (zipFile, "environment"); - Assert.IsNotNull (data, "environment should exist in the apk."); - var env = Encoding.ASCII.GetString (data); - var lines = env.Split (new char [] { '\n' }); + string javaEnv = Path.Combine (Root, appb.ProjectDirectory, + app.IntermediateOutputPath, "android", "src", "mono", "android", "app", "XamarinAndroidEnvironmentVariables.java"); + Assert.IsTrue (File.Exists (javaEnv), $"Java environment source does not exist at {javaEnv}"); - Assert.IsTrue (lines.Any (x => x.Contains ("MONO_DEBUG") && + string[] lines = File.ReadAllLines (javaEnv); + Assert.IsTrue (lines.Any (x => x.Contains ("MONO_DEBUG") && x.Contains ("soft-breakpoints") && string.IsNullOrEmpty (sequencePointsMode) ? true : x.Contains ("gen-compact-seq-points")), "The values from Mono.env should have been merged into environment"); - } + + string dexFile = Path.Combine (Root, appb.ProjectDirectory, app.IntermediateOutputPath, "android", "bin", "classes.dex"); + Assert.IsTrue (File.Exists (dexFile), $"dex file does not exist at {dexFile}"); + Assert.IsTrue (DexUtils.ContainsClass ("Lmono/android/app/XamarinAndroidEnvironmentVariables;", dexFile, appb.AndroidSdkDirectory), + $"dex file {dexFile} does not contain the XamarinAndroidEnvironmentVariables class"); + var assemblyDir = Path.Combine (Root, appb.ProjectDirectory, app.IntermediateOutputPath, "android", "assets"); var rp = new ReaderParameters { ReadSymbols = false }; foreach (var assemblyFile in Directory.EnumerateFiles (assemblyDir, "*.dll")) { @@ -1811,21 +1813,22 @@ public void CheckMonoDebugIsAddedToEnvironment ([Values ("", "Normal", "Offline" using (var b = CreateApkBuilder (Path.Combine ("temp", TestName))) { b.Verbosity = LoggerVerbosity.Diagnostic; Assert.IsTrue (b.Build (proj), "Build should have succeeded."); - var apk = Path.Combine (Root, b.ProjectDirectory, - proj.IntermediateOutputPath, "android", "bin", "UnnamedProject.UnnamedProject.apk"); - using (var zipFile = ZipHelper.OpenZip (apk)) { - var data = ZipHelper.ReadFileFromZip (zipFile, "environment"); - Assert.IsNotNull (data, "environment should exist in the apk."); - var env = Encoding.ASCII.GetString (data); - var lines = env.Split (new char [] { '\n' }); - - Assert.IsTrue (lines.Any (x => - string.IsNullOrEmpty (sequencePointsMode) - ? !x.Contains ("MONO_DEBUG") - : x.Contains ("MONO_DEBUG") && x.Contains ("gen-compact-seq-points")), - "environment {0} contain MONO_DEBUG=gen-compact-seq-points", - string.IsNullOrEmpty (sequencePointsMode) ? "should not" : "should"); - } + string javaEnv = Path.Combine (Root, b.ProjectDirectory, + proj.IntermediateOutputPath, "android", "src", "mono", "android", "app", "XamarinAndroidEnvironmentVariables.java"); + Assert.IsTrue (File.Exists (javaEnv), $"Java environment source does not exist at {javaEnv}"); + + string[] lines = File.ReadAllLines (javaEnv); + Assert.IsTrue (lines.Any (x => + string.IsNullOrEmpty (sequencePointsMode) + ? !x.Contains ("MONO_DEBUG") + : x.Contains ("MONO_DEBUG") && x.Contains ("gen-compact-seq-points")), + "environment {0} contain MONO_DEBUG=gen-compact-seq-points", + string.IsNullOrEmpty (sequencePointsMode) ? "should not" : "should"); + + string dexFile = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "bin", "classes.dex"); + Assert.IsTrue (File.Exists (dexFile), $"dex file does not exist at {dexFile}"); + Assert.IsTrue (DexUtils.ContainsClass ("Lmono/android/app/XamarinAndroidEnvironmentVariables;", dexFile, b.AndroidSdkDirectory), + $"dex file {dexFile} does not contain the XamarinAndroidEnvironmentVariables class"); } } diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs index 5be56daff8e..7d092cde444 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs @@ -67,19 +67,26 @@ public void CheckBuildIdIsUnique () //NOTE: Windows is still generating mdb files here extension = IsWindows ? "dll.mdb" : "pdb"; Assert.IsTrue (allFilesInArchive.Any (x => Path.GetFileName (x) == $"{proj.ProjectName}.{extension}"), $"{proj.ProjectName}.{extension} should exist in {archivePath}"); - foreach (var abi in new string [] { "armeabi-v7a", "x86" }) { - using (var apk = ZipHelper.OpenZip (Path.Combine (outputPath, proj.PackageName + "-" + abi + "-Signed.apk"))) { - var data = ZipHelper.ReadFileFromZip (apk, "environment"); - var env = Encoding.ASCII.GetString (data); - var lines = env.Split (new char [] { '\n' }); - Assert.IsTrue (lines.Any (x => x.Contains ("XAMARIN_BUILD_ID")), - "The environment should contain a XAMARIN_BUIL_ID"); - var buildID = lines.First (x => x.StartsWith ("XAMARIN_BUILD_ID", StringComparison.InvariantCultureIgnoreCase)); - buildIds.Add (abi, buildID); - } - } - Assert.IsFalse (buildIds.Values.Any (x => buildIds.Values.Any (v => v != x)), - "All the XAMARIN_BUILD_ID values should be the same"); + string javaEnv = Path.Combine (Root, b.ProjectDirectory, + proj.IntermediateOutputPath, "android", "src", "mono", "android", "app", "XamarinAndroidEnvironmentVariables.java"); + Assert.IsTrue (File.Exists (javaEnv), $"Java environment source does not exist at {javaEnv}"); + + string[] lines = File.ReadAllLines (javaEnv); + + Assert.IsTrue (lines.Any (x => x.Contains ("\"XAMARIN_BUILD_ID\",")), + "The environment should contain a XAMARIN_BUILD_ID"); + + string buildID = lines.First (x => x.Contains ("\"XAMARIN_BUILD_ID\",")) + .Trim () + .Replace ("\", \"", "=") + .Replace ("\",", String.Empty) + .Replace ("\"", String.Empty); + buildIds.Add ("all", buildID); + + string dexFile = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath, "android", "bin", "classes.dex"); + Assert.IsTrue (File.Exists (dexFile), $"dex file does not exist at {dexFile}"); + Assert.IsTrue (DexUtils.ContainsClass ("Lmono/android/app/XamarinAndroidEnvironmentVariables;", dexFile, b.AndroidSdkDirectory), + $"dex file {dexFile} does not contain the XamarinAndroidEnvironmentVariables class"); var msymDirectory = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, proj.PackageName + ".apk.mSYM"); Assert.IsTrue (File.Exists (Path.Combine (msymDirectory, "manifest.xml")), "manifest.xml should exist in", msymDirectory); diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj index f16e8a78db9..6ea2fd58700 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj @@ -686,6 +686,9 @@ ResourcePatcher.java + + XamarinAndroidEnvironmentVariables.java + diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 5674548136a..e8fcb6e2ab2 100755 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -2341,10 +2341,21 @@ because xbuild doesn't support framework reference assemblies. ResolvedAssemblies="@(_ResolvedAssemblies)" ResolvedUserAssemblies="@(_ResolvedUserAssemblies)" MainAssembly="$(MonoAndroidLinkerInputDir)$(TargetFileName)" - OutputDirectory="$(IntermediateOutputPath)android\src\mono" + OutputDirectory="$(IntermediateOutputPath)android\src\mono" + EnvironmentOutputDirectory="$(IntermediateOutputPath)android\src\mono\android\app" UseSharedRuntime="$(AndroidUseSharedRuntime)" TargetFrameworkVersion="$(TargetFrameworkVersion)" - Manifest="$(IntermediateOutputPath)android\AndroidManifest.xml" /> + Manifest="$(IntermediateOutputPath)android\AndroidManifest.xml" + Environments="@(AndroidEnvironment);@(LibraryEnvironments)" + AndroidAotMode="$(AndroidAotMode)" + EnableLLVM="$(EnableLLVM)" + HttpClientHandlerType="$(AndroidHttpClientHandlerType)" + TlsProvider="$(AndroidTlsProvider)" + Debug="$(AndroidIncludeDebugSymbols)" + AndroidSequencePointsMode="$(_SequencePointsMode)" + EnableSGenConcurrent="$(AndroidEnableSGenConcurrent)"> + + @@ -2866,7 +2877,6 @@ because xbuild doesn't support framework reference assemblies. BundleAssemblies="$(BundleAssemblies)" BundleNativeLibraries="$(_BundleResultNativeLibraries)" EmbedAssemblies="$(EmbedAssembliesIntoApk)" - Environments="@(AndroidEnvironment);@(LibraryEnvironments)" ResolvedUserAssemblies="@(_ResolvedUserAssemblies);@(_AndroidResolvedSatellitePaths)" ResolvedFrameworkAssemblies="@(_ShrunkFrameworkAssemblies)" NativeLibraries="@(AndroidNativeLibrary)" @@ -2879,18 +2889,13 @@ because xbuild doesn't support framework reference assemblies. Debug="$(AndroidIncludeDebugSymbols)" PreferNativeLibrariesWithDebugSymbols="$(AndroidPreferNativeLibrariesWithDebugSymbols)" TypeMappings="$(_AndroidTypeMappingJavaToManaged);$(_AndroidTypeMappingManagedToJava)" - AndroidAotMode="$(AndroidAotMode)" - EnableLLVM="$(EnableLLVM)" JavaSourceFiles="@(AndroidJavaSource)" JavaLibraries="@(AndroidJavaLibrary)" AndroidSequencePointsMode="$(_SequencePointsMode)" LibraryProjectJars="@(ExtractedJarImports)" AndroidEmbedProfilers="$(AndroidEmbedProfilers)" - HttpClientHandlerType="$(AndroidHttpClientHandlerType)" TlsProvider="$(AndroidTlsProvider)" - UncompressedFileExtensions="$(AndroidStoreUncompressedFileExtensions)" - EnableSGenConcurrent="$(AndroidEnableSGenConcurrent)"> - + UncompressedFileExtensions="$(AndroidStoreUncompressedFileExtensions)"> diff --git a/src/monodroid/CMakeLists.txt b/src/monodroid/CMakeLists.txt index 2be6fac7403..5ccddcd5cbd 100644 --- a/src/monodroid/CMakeLists.txt +++ b/src/monodroid/CMakeLists.txt @@ -89,6 +89,9 @@ set(TEST_COMPILER_ARGS finline-limit=300 fvisibility=hidden fstack-protector + fno-rtti + fno-exceptions + flto Wa,--noexecstack Wformat Werror=format-security @@ -238,6 +241,7 @@ set(SOURCES_DIR ${TOP_DIR}/jni) set(MONODROID_SOURCES ${MONODROID_SOURCES} ${MONO_PATH}/support/zlib-helper.c + ${SOURCES_DIR}/new_delete.cc ${SOURCES_DIR}/android-system.cc ${SOURCES_DIR}/cpu-arch-detect.cc ${SOURCES_DIR}/debug-constants.cc diff --git a/src/monodroid/jni/android-system.cc b/src/monodroid/jni/android-system.cc index a0855415a09..48537359bf5 100644 --- a/src/monodroid/jni/android-system.cc +++ b/src/monodroid/jni/android-system.cc @@ -1,8 +1,8 @@ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #ifdef ANDROID @@ -23,6 +23,7 @@ #include "android-system.h" #include "monodroid.h" #include "monodroid-glue-internal.h" +#include "jni-wrappers.h" using namespace xamarin::android; using namespace xamarin::android::internal; @@ -40,7 +41,7 @@ constexpr char AndroidSystem::MONO_SGEN_SO[]; constexpr char AndroidSystem::MONO_SGEN_ARCH_SO[]; #if defined (WINDOWS) -pthread_mutex_t AndroidSystem::readdir_mutex = PTHREAD_MUTEX_INITIALIZER; +std::mutex AndroidSystem::readdir_mutex; char *AndroidSystem::libmonoandroid_directory_path = nullptr; #endif @@ -396,12 +397,12 @@ AndroidSystem::get_libmonosgen_path () // storage location before loading it. copy_native_libraries_to_internal_location (); - if (!embedded_dso_mode) { + if (!is_embedded_dso_mode_enabled ()) { for (i = 0; i < MAX_OVERRIDES; ++i) TRY_LIBMONOSGEN (override_dirs [i]); } #endif - if (!embedded_dso_mode) { + if (!is_embedded_dso_mode_enabled ()) { for (i = 0; i < app_lib_directories_size; i++) { TRY_LIBMONOSGEN (app_lib_directories [i]); } @@ -450,7 +451,7 @@ AndroidSystem::get_libmonosgen_path () log_fatal (LOG_DEFAULT, "Cannot find '%s'. Looked in the following locations:", MONO_SGEN_SO); #ifndef RELEASE - if (!embedded_dso_mode) { + if (!is_embedded_dso_mode_enabled ()) { for (i = 0; i < MAX_OVERRIDES; ++i) { if (override_dirs [i] == NULL) continue; @@ -493,14 +494,14 @@ AndroidSystem::load_dso (const char *path, int dl_flags, mono_bool skip_exists_c return NULL; log_info (LOG_ASSEMBLY, "Trying to load shared library '%s'", path); - if (!skip_exists_check && !embedded_dso_mode && !utils.file_exists (path)) { + if (!skip_exists_check && !is_embedded_dso_mode_enabled () && !utils.file_exists (path)) { log_info (LOG_ASSEMBLY, "Shared library '%s' not found", path); return NULL; } void *handle = dlopen (path, dl_flags); - if (handle == NULL) - log_info (LOG_ASSEMBLY, "Failed to load shared library '%s'. %s", path, dlerror ()); + if (handle == NULL && utils.should_log (LOG_ASSEMBLY)) + log_info_nocheck (LOG_ASSEMBLY, "Failed to load shared library '%s'. %s", path, dlerror ()); return handle; } @@ -583,7 +584,7 @@ AndroidSystem::get_full_dso_path_on_disk (const char *dso_name, mono_bool *needs assert (needs_free); *needs_free = FALSE; - if (embedded_dso_mode) + if (is_embedded_dso_mode_enabled ()) return NULL; char *dso_path = nullptr; @@ -731,89 +732,95 @@ AndroidSystem::get_gref_gc_threshold () } void -AndroidSystem::setup_environment_from_line (const char *line) +AndroidSystem::setup_environment (jstring_wrapper& name, jstring_wrapper& value) { - char **entry; - const char *k, *v; + const char *k = name.get_cstr (); - if (line == NULL || !isprint (line [0])) + if (k == nullptr || *k == '\0') return; - entry = utils.monodroid_strsplit (line, "=", 2); + const char *v = value.get_cstr (); + if (v == nullptr || *v == '\0') + v = ""; - if ((k = entry [0]) && *k && - (v = entry [1]) && *v) { - if (islower (k [0])) { - add_system_property (k, v); - } else { - setenv (k, v, 1); + if (isupper (k [0]) || k [0] == '_') { + if (k [0] == '_') { + if (strcmp (k, "__XA_DSO_IN_APK") == 0) { + knownEnvVars.DSOInApk = true; + return; + } } - } - - utils.monodroid_strfreev (entry); -} -void -AndroidSystem::setup_environment_from_file (const char *apk, int index, int apk_count, void *user_data) -{ - unzFile file; - if ((file = unzOpen (apk)) == nullptr) + setenv (k, v, 1); return; + } - if (unzLocateFile (file, "environment", 0) == UNZ_OK) { - unz_file_info info; + if (k [0] == 'm') { + if (strcmp (k, "mono.aot") == 0) { + if (*v == '\0') { + knownEnvVars.MonoAOT = MonoAotMode::MONO_AOT_MODE_NONE; + return; + } - if (unzGetCurrentFileInfo (file, &info, nullptr, 0, nullptr, 0, nullptr, 0) == UNZ_OK && - unzOpenCurrentFile (file) == UNZ_OK) { - char *contents = new char [info.uncompressed_size+1]; - if (contents != NULL && - unzReadCurrentFile (file, contents, info.uncompressed_size) > 0) { + switch (v [0]) { + case 'n': + knownEnvVars.MonoAOT = MonoAotMode::MONO_AOT_MODE_NORMAL; + break; - int i; - char *line = contents; - contents [info.uncompressed_size] = '\0'; + case 'h': + knownEnvVars.MonoAOT = MonoAotMode::MONO_AOT_MODE_HYBRID; + break; - for (i = 0; i < info.uncompressed_size; ++i) { - if (contents [i] != '\n') - continue; + case 'f': + knownEnvVars.MonoAOT = MonoAotMode::MONO_AOT_MODE_FULL; + break; - contents [i] = '\0'; - setup_environment_from_line (line); - line = &contents [i+1]; - } + default: + knownEnvVars.MonoAOT = MonoAotMode::MONO_AOT_MODE_UNKNOWN; + break; + } - if (line < (contents + info.uncompressed_size)) - setup_environment_from_line (line); + if (knownEnvVars.MonoAOT != MonoAotMode::MONO_AOT_MODE_UNKNOWN) + log_info (LOG_DEFAULT, "Mono AOT mode: %s", v); + else + log_warn (LOG_DEFAULT, "Unknown Mono AOT mode: %s", v); - free (contents); - } + return; + } - unzCloseCurrentFile (file); + if (strcmp (k, "mono.llvm") == 0) { + knownEnvVars.MonoLLVM = true; + return; } } - unzClose (file); + add_system_property (k, v); } void -AndroidSystem::for_each_apk (JNIEnv *env, jobjectArray runtimeApks, void (AndroidSystem::*handler) (const char *apk, int index, int apk_count, void *user_data), void *user_data) +AndroidSystem::setup_environment (JNIEnv *env, jobjectArray environmentVariables) { - int i; - jsize apksLength = env->GetArrayLength (runtimeApks); - for (i = 0; i < apksLength; ++i) { - jstring e = reinterpret_cast (env->GetObjectArrayElement (runtimeApks, i)); - const char *apk = env->GetStringUTFChars (e, nullptr); - + jsize envvarsLength = env->GetArrayLength (environmentVariables); + if (envvarsLength == 0) + return; - (this->*handler) (apk, i, apksLength, user_data); - env->ReleaseStringUTFChars (e, apk); + jstring_wrapper name (env), value (env); + for (jsize i = 0; (i + 1) < envvarsLength; i += 2) { + name = reinterpret_cast (env->GetObjectArrayElement (environmentVariables, i)); + value = reinterpret_cast (env->GetObjectArrayElement (environmentVariables, i + 1)); + setup_environment (name, value); } } void -AndroidSystem::setup_environment (JNIEnv *env, jobjectArray runtimeApks) +AndroidSystem::for_each_apk (JNIEnv *env, jstring_array_wrapper &runtimeApks, void (AndroidSystem::*handler) (const char *apk, int index, int apk_count, void *user_data), void *user_data) { - for_each_apk (env, runtimeApks, &AndroidSystem::setup_environment_from_file, NULL); + size_t apksLength = runtimeApks.get_length (); + for (size_t i = 0; i < apksLength; ++i) { + jstring_wrapper &e = runtimeApks [i]; + + (this->*handler) (e.get_cstr (), i, apksLength, user_data); + } } void @@ -827,7 +834,7 @@ AndroidSystem::setup_process_args_apk (const char *apk, int index, int apk_count } void -AndroidSystem::setup_process_args (JNIEnv *env, jobjectArray runtimeApks) +AndroidSystem::setup_process_args (JNIEnv *env, jstring_array_wrapper &runtimeApks) { for_each_apk (env, runtimeApks, &AndroidSystem::setup_process_args_apk, NULL); } @@ -841,7 +848,7 @@ AndroidSystem::add_apk_libdir (const char *apk, int index, int apk_count, void * } void -AndroidSystem::setup_apk_directories (JNIEnv *env, unsigned short running_on_cpu, jobjectArray runtimeApks) +AndroidSystem::setup_apk_directories (JNIEnv *env, unsigned short running_on_cpu, jstring_array_wrapper &runtimeApks) { // Man, the cast is ugly... for_each_apk (env, runtimeApks, &AndroidSystem::add_apk_libdir, const_cast (static_cast (android_abi_names [running_on_cpu]))); @@ -858,7 +865,7 @@ AndroidSystem::readdir_r (_WDIR *dirp, struct _wdirent *entry, struct _wdirent * { int error_code = 0; - pthread_mutex_lock (&readdir_mutex); + std::lock_guard lock (readdir_mutex); errno = 0; entry = _wreaddir (dirp); *result = entry; @@ -866,7 +873,6 @@ AndroidSystem::readdir_r (_WDIR *dirp, struct _wdirent *entry, struct _wdirent * if (entry == NULL && errno != 0) error_code = -1; - pthread_mutex_unlock (&readdir_mutex); return error_code; } diff --git a/src/monodroid/jni/android-system.h b/src/monodroid/jni/android-system.h index 898b9ff1641..32f8f6fcb1b 100644 --- a/src/monodroid/jni/android-system.h +++ b/src/monodroid/jni/android-system.h @@ -2,14 +2,20 @@ #ifndef __ANDROID_SYSTEM_H #define __ANDROID_SYSTEM_H -#include -#include +#include +#include #include #include #include "dylib-mono.h" #include "util.h" #include "cpu-arch.h" +#include "cppcompat.h" + +namespace xamarin { namespace android { + class jstring_wrapper; + class jstring_array_wrapper; +}} namespace xamarin { namespace android { namespace internal { @@ -20,13 +26,20 @@ namespace xamarin { namespace android { namespace internal struct BundledProperty *next; }; + struct KnownEnvironmentVariables + { + bool DSOInApk = false; + MonoAotMode MonoAOT = MonoAotMode::MONO_AOT_MODE_NONE; + bool MonoLLVM = false; + }; + class AndroidSystem { private: static BundledProperty *bundled_properties; static const char* android_abi_names[CPU_KIND_X86_64+1]; #if defined (WINDOWS) - static pthread_mutex_t readdir_mutex; + static std::mutex readdir_mutex; static char *libmonoandroid_directory_path; #endif @@ -73,8 +86,8 @@ namespace xamarin { namespace android { namespace internal public: void add_system_property (const char *name, const char *value); - void setup_environment (JNIEnv *env, jobjectArray runtimeApks); - void setup_process_args (JNIEnv *env, jobjectArray runtimeApks); + void setup_environment (JNIEnv *env, jobjectArray environmentVariables); + void setup_process_args (JNIEnv *env, jstring_array_wrapper &runtimeApks); int monodroid_get_system_property (const char *name, char **value); int monodroid_get_system_property_from_overrides (const char *name, char ** value); int monodroid_read_file_into_memory (const char *path, char **value); @@ -83,7 +96,7 @@ namespace xamarin { namespace android { namespace internal char* get_bundled_app (JNIEnv *env, jstring dir); int count_override_assemblies (); int get_gref_gc_threshold (); - void setup_apk_directories (JNIEnv *env, unsigned short running_on_cpu, jobjectArray runtimeApks); + void setup_apk_directories (JNIEnv *env, unsigned short running_on_cpu, jstring_array_wrapper &runtimeApks); void* load_dso (const char *path, int dl_flags, mono_bool skip_exists_check); void* load_dso_from_any_directories (const char *name, int dl_flags); char* get_full_dso_path_on_disk (const char *dso_name, mono_bool *needs_free); @@ -119,10 +132,24 @@ namespace xamarin { namespace android { namespace internal int setenv (const char *name, const char *value, int overwrite); #endif + bool is_mono_llvm_enabled () const + { + return knownEnvVars.MonoLLVM; + } + + bool is_embedded_dso_mode_enabled () const + { + return knownEnvVars.DSOInApk; + } + + MonoAotMode get_mono_aot_mode () const + { + return knownEnvVars.MonoAOT; + } + private: int get_max_gref_count_from_system (); - void setup_environment_from_line (const char *line); - void setup_environment_from_file (const char *apk, int index, int apk_count, void *user_data); + void setup_environment (jstring_wrapper& name, jstring_wrapper& value); BundledProperty* lookup_system_property (const char *name); void setup_process_args_apk (const char *apk, int index, int apk_count, void *user_data); int _monodroid__system_property_get (const char *name, char *sp_value, size_t sp_value_len); @@ -130,7 +157,7 @@ namespace xamarin { namespace android { namespace internal void copy_native_libraries_to_internal_location (); void copy_file_to_internal_location (char *to_dir, char *from_dir, char *file); void add_apk_libdir (const char *apk, int index, int apk_count, void *user_data); - void for_each_apk (JNIEnv *env, jobjectArray runtimeApks, void (AndroidSystem::*handler) (const char *apk, int index, int apk_count, void *user_data), void *user_data); + void for_each_apk (JNIEnv *env, jstring_array_wrapper &runtimeApks, void (AndroidSystem::*handler) (const char *apk, int index, int apk_count, void *user_data), void *user_data); char* get_full_dso_path (const char *base_dir, const char *dso_path, mono_bool *needs_free); void* load_dso_from_specified_dirs (const char **directories, int num_entries, const char *dso_name, int dl_flags); void* load_dso_from_app_lib_dirs (const char *name, int dl_flags); @@ -148,6 +175,7 @@ namespace xamarin { namespace android { namespace internal #endif // !ANDROID private: int max_gref_count = 0; + KnownEnvironmentVariables knownEnvVars; }; }}} #endif // !__ANDROID_SYSTEM_H diff --git a/src/monodroid/jni/cppcompat.h b/src/monodroid/jni/cppcompat.h index 274c7cea6a2..c7a4c863876 100644 --- a/src/monodroid/jni/cppcompat.h +++ b/src/monodroid/jni/cppcompat.h @@ -2,6 +2,8 @@ #ifndef __CPP_COMPAT_H #define __CPP_COMPAT_H +#include + // Since Android doesn't currently have any standard C++ library // and we don't want to use any implementation of it shipped in // source form with the NDK (for space reasons), this header will @@ -20,5 +22,50 @@ namespace std { return static_cast::type&&>(arg); } + + template + class lock_guard + { + public: + using mutex_type = TMutex; + + public: + lock_guard (const lock_guard&) = delete; + + explicit lock_guard (mutex_type& _mutex) + : _mutex (_mutex) + { + _mutex.lock (); + } + + ~lock_guard () + { + _mutex.unlock (); + } + + lock_guard& operator= (const lock_guard&) = delete; + + private: + mutex_type &_mutex; + }; + + class mutex + { + public: + mutex () noexcept = default; + ~mutex () noexcept = default; + + void lock () noexcept + { + pthread_mutex_lock (&_pmutex); + } + + void unlock () noexcept + { + pthread_mutex_unlock (&_pmutex); + } + private: + pthread_mutex_t _pmutex = PTHREAD_MUTEX_INITIALIZER; + }; } #endif diff --git a/src/monodroid/jni/debug.cc b/src/monodroid/jni/debug.cc index 98fcee6c7ae..3ab558362de 100644 --- a/src/monodroid/jni/debug.cc +++ b/src/monodroid/jni/debug.cc @@ -195,8 +195,8 @@ Debug::handle_server_connection (void) flags = 1; rv = setsockopt (listen_socket, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof (flags)); - if (rv == -1) { - log_info (LOG_DEFAULT, "Could not set SO_REUSEADDR on the listening socket (%s)", strerror (errno)); + if (rv == -1 && utils.should_log (LOG_DEFAULT)) { + log_info_nocheck (LOG_DEFAULT, "Could not set SO_REUSEADDR on the listening socket (%s)", strerror (errno)); // not a fatal failure } diff --git a/src/monodroid/jni/dylib-mono.cc b/src/monodroid/jni/dylib-mono.cc index a4b5b713e50..2fcfc182f52 100644 --- a/src/monodroid/jni/dylib-mono.cc +++ b/src/monodroid/jni/dylib-mono.cc @@ -60,6 +60,11 @@ bool DylibMono::init (void *libmono_handle) #define LOAD_SYMBOL(symbol) LOAD_SYMBOL_CAST(symbol, monodroid_ ##symbol ##_fptr) #define LOAD_SYMBOL_NO_PREFIX(symbol) LOAD_SYMBOL_CAST(symbol, symbol ##_fptr) + timing_period total_time; + if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { + total_time.mark_start (); + } + LOAD_SYMBOL(mono_add_internal_call) LOAD_SYMBOL(mono_assembly_get_image) LOAD_SYMBOL(mono_assembly_load_from_full) @@ -138,6 +143,13 @@ bool DylibMono::init (void *libmono_handle) LOAD_SYMBOL_CAST(mono_use_llvm, int*) LOAD_SYMBOL_NO_PREFIX(mono_aot_register_module) + if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { + total_time.mark_end (); + + timing_diff diff (total_time); + log_info_nocheck (LOG_TIMING, "DylibMono.init: end, total time; elapsed: %lis:%lu::%lu", diff.sec, diff.ms, diff.ns); + } + if (symbols_missing) { log_fatal (LOG_DEFAULT, "Failed to load some Mono symbols, aborting..."); exit (FATAL_EXIT_MONO_MISSING_SYMBOLS); diff --git a/src/monodroid/jni/dylib-mono.h b/src/monodroid/jni/dylib-mono.h index 5548165d212..6c84d313942 100644 --- a/src/monodroid/jni/dylib-mono.h +++ b/src/monodroid/jni/dylib-mono.h @@ -344,7 +344,9 @@ enum MonoAotMode { MONO_AOT_MODE_HYBRID, /* Enables full AOT mode, JIT is disabled and not allowed, * equivalent to mono_jit_set_aot_only (true) */ - MONO_AOT_MODE_FULL + MONO_AOT_MODE_FULL, + + MONO_AOT_MODE_UNKNOWN = 0xBADBAD }; #ifndef __cplusplus typedef int MonoAotMode; diff --git a/src/monodroid/jni/embedded-assemblies.cc b/src/monodroid/jni/embedded-assemblies.cc index 0e68779dd5a..48fbea6c36c 100644 --- a/src/monodroid/jni/embedded-assemblies.cc +++ b/src/monodroid/jni/embedded-assemblies.cc @@ -1,7 +1,7 @@ #include #include -#include -#include +#include +#include #include #include #include @@ -72,7 +72,7 @@ open_from_bundles (MonoAssemblyName *aname, char **assemblies_path, void *user_d MonoAssembly *a = NULL; int name_len = culture == NULL ? 0 : strlen (culture) + 1; - name_len += std::strlen (reinterpret_cast (mono->assembly_name_get_name (aname))); + name_len += strlen (reinterpret_cast (mono->assembly_name_get_name (aname))); name = static_cast (utils.xmalloc (name_len + sizeof (".exe") + 1)); if (culture != NULL && strlen (culture) > 0) sprintf (name, "%s/%s", culture, (const char*) mono->assembly_name_get_name (aname)); @@ -108,8 +108,8 @@ open_from_bundles (MonoAssemblyName *aname, char **assemblies_path, void *user_d } } free (name); - if (a) { - log_info (LOG_ASSEMBLY, "open_from_bundles: loaded assembly: %p\n", a); + if (a && utils.should_log (LOG_ASSEMBLY)) { + log_info_nocheck (LOG_ASSEMBLY, "open_from_bundles: loaded assembly: %p\n", a); } return a; } @@ -139,7 +139,7 @@ monodroid_embedded_assemblies_install_preload_hook (DylibMono *imports) static int TypeMappingInfo_compare_key (const void *a, const void *b) { - return std::strcmp (reinterpret_cast (a), reinterpret_cast (b)); + return strcmp (reinterpret_cast (a), reinterpret_cast (b)); } MONO_API const char * @@ -148,7 +148,7 @@ monodroid_typemap_java_to_managed (const char *java) struct TypeMappingInfo *info; for (info = java_to_managed_maps; info != NULL; info = info->next) { /* log_warn (LOG_DEFAULT, "# jonp: checking file: %s!%s for type '%s'", info->source_apk, info->source_entry, java); */ - const char *e = reinterpret_cast (std::bsearch (java, info->mapping, info->num_entries, info->entry_length, TypeMappingInfo_compare_key)); + const char *e = reinterpret_cast (bsearch (java, info->mapping, info->num_entries, info->entry_length, TypeMappingInfo_compare_key)); if (e == NULL) continue; return e + info->value_offset; @@ -162,7 +162,7 @@ monodroid_typemap_managed_to_java (const char *managed) struct TypeMappingInfo *info; for (info = managed_to_java_maps; info != NULL; info = info->next) { /* log_warn (LOG_DEFAULT, "# jonp: checking file: %s!%s for type '%s'", info->source_apk, info->source_entry, managed); */ - const char *e = reinterpret_cast (std::bsearch (managed, info->mapping, info->num_entries, info->entry_length, TypeMappingInfo_compare_key)); + const char *e = reinterpret_cast (bsearch (managed, info->mapping, info->num_entries, info->entry_length, TypeMappingInfo_compare_key)); if (e == NULL) continue; return e + info->value_offset; @@ -480,7 +480,7 @@ gather_bundled_assemblies_from_apk ( psize = (unsigned int*) &cur->size; *psize = info.uncompressed_size; - if ((log_categories & LOG_ASSEMBLY) != 0) { + if (utils.should_log (LOG_ASSEMBLY)) { const char *p = (const char*) cur->data; char header[9]; @@ -489,7 +489,7 @@ gather_bundled_assemblies_from_apk ( header[i] = isprint (p [i]) ? p [i] : '.'; header [sizeof(header)-1] = '\0'; - log_info (LOG_ASSEMBLY, "file-offset: % 8x start: %08p end: %08p len: % 12i zip-entry: %s name: %s [%s]", + log_info_nocheck (LOG_ASSEMBLY, "file-offset: % 8x start: %08p end: %08p len: % 12i zip-entry: %s name: %s [%s]", (int) offset, cur->data, cur->data + *psize, (int) info.uncompressed_size, cur_entry_name, cur->name, header); } diff --git a/src/monodroid/jni/globals.h b/src/monodroid/jni/globals.h index 3220ed02fe1..a759a47bbbd 100644 --- a/src/monodroid/jni/globals.h +++ b/src/monodroid/jni/globals.h @@ -6,6 +6,7 @@ #include "util.h" #include "debug.h" #include "monodroid-glue-internal.h" +#include "cppcompat.h" extern xamarin::android::DylibMono monoFunctions; extern xamarin::android::Util utils; diff --git a/src/monodroid/jni/jni-wrappers.h b/src/monodroid/jni/jni-wrappers.h new file mode 100644 index 00000000000..9a7cfed4cd7 --- /dev/null +++ b/src/monodroid/jni/jni-wrappers.h @@ -0,0 +1,179 @@ +// Dear Emacs, this is a -*- C++ -*- header +#ifndef __JNI_WRAPPERS_H +#define __JNI_WRAPPERS_H + +#include +#include +#include + +#ifdef __cplusplus + +namespace xamarin { namespace android +{ + class jstring_array_wrapper; + + class jstring_wrapper + { + public: + explicit jstring_wrapper (JNIEnv *env) noexcept + : env (env), + jstr (nullptr) + { + assert (env); + } + + explicit jstring_wrapper (JNIEnv *env, const jobject jo) noexcept + : env (env), + jstr (reinterpret_cast (jo)) + { + assert (env); + } + + explicit jstring_wrapper (JNIEnv *env, const jstring js) noexcept + : env (env), + jstr (js) + { + assert (env); + } + + jstring_wrapper (const jstring_wrapper&) = delete; + + ~jstring_wrapper () noexcept + { + release (); + } + + jstring_wrapper& operator=(const jstring_wrapper&) = delete; + + const char* get_cstr () noexcept + { + if (cstr == nullptr && env != nullptr) + cstr = env->GetStringUTFChars (jstr, nullptr); + + return cstr; + } + + jstring_wrapper& operator= (const jobject new_jo) noexcept + { + assign (reinterpret_cast (new_jo)); + return *this; + } + + jstring_wrapper& operator= (const jstring new_js) noexcept + { + assign (new_js); + return *this; + } + + protected: + void release () noexcept + { + if (jstr == nullptr || cstr == nullptr || env == nullptr) + return; + env->ReleaseStringUTFChars (jstr, cstr); + jobjectRefType type = env->GetObjectRefType (jstr); + switch (type) { + case JNILocalRefType: + env->DeleteLocalRef (jstr); + break; + + case JNIGlobalRefType: + env->DeleteGlobalRef (jstr); + break; + + case JNIWeakGlobalRefType: + env->DeleteWeakGlobalRef (jstr); + break; + + case JNIInvalidRefType: // To hush compiler warning + break; + } + + jstr = nullptr; + cstr = nullptr; + } + + void assign (const jstring new_js) noexcept + { + release (); + if (new_js == nullptr) + return; + + jstr = new_js; + cstr = nullptr; + } + + friend class jstring_array_wrapper; + + private: + jstring_wrapper () + : env (nullptr), + jstr (nullptr) + {} + + private: + JNIEnv *env; + jstring jstr; + const char *cstr = nullptr; + }; + + class jstring_array_wrapper + { + public: + explicit jstring_array_wrapper (JNIEnv *env) noexcept + : env (env), + arr (nullptr), + len (0) + { + assert (env); + } + + explicit jstring_array_wrapper (JNIEnv *env, jobjectArray arr) + : env (env), + arr (arr) + { + assert (env); + assert (arr); + len = env->GetArrayLength (arr); + if (len > sizeof (static_wrappers) / sizeof (jstring_wrapper)) + wrappers = new jstring_wrapper [len]; + else + wrappers = static_wrappers; + } + + ~jstring_array_wrapper () noexcept + { + if (wrappers != nullptr && wrappers != static_wrappers) + delete[] wrappers; + } + + size_t get_length () const noexcept + { + return len; + } + + jstring_wrapper& operator[] (size_t index) noexcept + { + if (index >= len) + return invalid_wrapper; + + if (wrappers [index].env == nullptr) { + wrappers [index].env = env; + wrappers [index].jstr = reinterpret_cast (env->GetObjectArrayElement (arr, index)); + } + + return wrappers [index]; + } + + private: + JNIEnv *env; + jobjectArray arr; + size_t len; + jstring_wrapper *wrappers; + jstring_wrapper static_wrappers[5]; + jstring_wrapper invalid_wrapper; + }; +}} + +#endif // __cplusplus +#endif // __JNI_WRAPPERS_H diff --git a/src/monodroid/jni/mono_android_Runtime.h b/src/monodroid/jni/mono_android_Runtime.h index 8bd4ae1bf3c..e634391795e 100644 --- a/src/monodroid/jni/mono_android_Runtime.h +++ b/src/monodroid/jni/mono_android_Runtime.h @@ -10,10 +10,10 @@ extern "C" { /* * Class: mono_android_Runtime * Method: init - * Signature: (Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/ClassLoader;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V + * Signature: (Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/ClassLoader;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;I;Ljava/lang/String)V */ JNIEXPORT void JNICALL Java_mono_android_Runtime_init - (JNIEnv *, jclass, jstring, jobjectArray, jstring, jobjectArray, jobject, jobjectArray, jobjectArray, jstring); + (JNIEnv *, jclass, jstring, jobjectArray, jstring, jobjectArray, jobject, jobjectArray, jobjectArray, jstring, jint, jobjectArray); /* * Class: mono_android_Runtime diff --git a/src/monodroid/jni/monodroid-glue-internal.h b/src/monodroid/jni/monodroid-glue-internal.h index 8bc69047202..cf27decf1fc 100644 --- a/src/monodroid/jni/monodroid-glue-internal.h +++ b/src/monodroid/jni/monodroid-glue-internal.h @@ -13,7 +13,6 @@ namespace xamarin { namespace android { namespace internal extern char *external_override_dir; extern char *external_legacy_override_dir; extern char *runtime_libdir; - extern int embedded_dso_mode; class MonodroidRuntime { diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index bf16bdbe05d..76b3b90c71e 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -1,4 +1,3 @@ - #include #include #include @@ -159,23 +158,15 @@ monodroid_get_system_property (const char *name, char **value) } static char* -get_primary_override_dir (JNIEnv *env, jstring home) +get_primary_override_dir (JNIEnv *env, jstring_wrapper &home) { - const char *v; - char *p; - - v = env->GetStringUTFChars (home, NULL); - p = utils.path_combine (v, ".__override__"); - env->ReleaseStringUTFChars (home, v); - - return p; + return utils.path_combine (home.get_cstr (), ".__override__"); } // TODO: these must be moved to some class char *xamarin::android::internal::primary_override_dir; char *xamarin::android::internal::external_override_dir; char *xamarin::android::internal::external_legacy_override_dir; -int xamarin::android::internal::embedded_dso_mode = 0; /* Set of Windows-specific utility/reimplementation of Unix functions */ #ifdef WINDOWS @@ -230,7 +221,7 @@ setup_bundled_app (const char *dso_name) static int dlopen_flags = RTLD_LAZY; void *libapp = NULL; - if (embedded_dso_mode) { + if (androidSystem.is_embedded_dso_mode_enabled ()) { log_info (LOG_DEFAULT, "bundle app: embedded DSO mode"); libapp = androidSystem.load_dso_from_any_directories (dso_name, dlopen_flags); } else { @@ -247,7 +238,7 @@ setup_bundled_app (const char *dso_name) if (libapp == NULL) { log_info (LOG_DEFAULT, "No libapp!"); - if (!embedded_dso_mode) { + if (!androidSystem.is_embedded_dso_mode_enabled ()) { log_fatal (LOG_BUNDLE, "bundled app initialization error"); exit (FATAL_EXIT_CANNOT_LOAD_BUNDLE); } else { @@ -273,10 +264,13 @@ typedef struct { static MonoDroidProfiler monodroid_profiler; static jclass TimeZone_class; -static jmethodID TimeZone_getDefault; -static jmethodID TimeZone_getID; -static int is_running_on_desktop = 0; +static constexpr bool is_running_on_desktop = +#if ANDROID + false; +#else + true; +#endif MONO_API int _monodroid_max_gref_get (void) @@ -437,8 +431,8 @@ open_from_update_dir (MonoAssemblyName *aname, char **assemblies_path, void *use } } free (pname); - if (result) { - log_info (LOG_ASSEMBLY, "open_from_update_dir: loaded assembly: %p\n", result); + if (result && utils.should_log (LOG_ASSEMBLY)) { + log_info_nocheck (LOG_ASSEMBLY, "open_from_update_dir: loaded assembly: %p\n", result); } return result; } @@ -471,16 +465,12 @@ should_register_file (const char *filename, void *user_data) } static void -gather_bundled_assemblies (JNIEnv *env, jobjectArray runtimeApks, mono_bool register_debug_symbols, int *out_user_assemblies_count) +gather_bundled_assemblies (JNIEnv *env, jstring_array_wrapper &runtimeApks, mono_bool register_debug_symbols, int *out_user_assemblies_count) { - jsize i; - int prev_num_assemblies = 0; - jsize apksLength = env->GetArrayLength (runtimeApks); - monodroid_embedded_assemblies_set_register_debug_symbols (register_debug_symbols); monodroid_embedded_assemblies_set_should_register (should_register_file, NULL); #ifndef RELEASE - for (i = 0; i < AndroidSystem::MAX_OVERRIDES; ++i) { + for (size_t i = 0; i < AndroidSystem::MAX_OVERRIDES; ++i) { const char *p = androidSystem.get_override_dir (i); if (!utils.directory_exists (p)) continue; @@ -488,22 +478,18 @@ gather_bundled_assemblies (JNIEnv *env, jobjectArray runtimeApks, mono_bool regi try_load_typemaps_from_directory (p); } #endif - for (i = apksLength - 1; i >= 0; --i) { - int cur_num_assemblies; - const char *apk_file; - jstring apk = reinterpret_cast (env->GetObjectArrayElement (runtimeApks, i)); - apk_file = env->GetStringUTFChars (apk, NULL); + int prev_num_assemblies = 0; + for (int32_t i = runtimeApks.get_length () - 1; i >= 0; --i) { + int cur_num_assemblies; + jstring_wrapper &apk_file = runtimeApks [i]; - cur_num_assemblies = monodroid_embedded_assemblies_register_from (&monoFunctions, apk_file); + cur_num_assemblies = monodroid_embedded_assemblies_register_from (&monoFunctions, apk_file.get_cstr ()); - if (strstr (apk_file, "/Mono.Android.DebugRuntime") == NULL && - strstr (apk_file, "/Mono.Android.Platform.ApiLevel_") == NULL) + if (strstr (apk_file.get_cstr (), "/Mono.Android.DebugRuntime") == nullptr && + strstr (apk_file.get_cstr (), "/Mono.Android.Platform.ApiLevel_") == nullptr) *out_user_assemblies_count += (cur_num_assemblies - prev_num_assemblies); prev_num_assemblies = cur_num_assemblies; - - env->ReleaseStringUTFChars (apk, apk_file); - env->DeleteLocalRef (apk); } } @@ -592,38 +578,6 @@ JNI_OnLoad (JavaVM *vm, void *reserved) vm->GetEnv ((void**)&env, JNI_VERSION_1_6); osBridge.initialize_on_onload (vm, env); - TimeZone_class = reinterpret_cast (osBridge.lref_to_gref (env, env->FindClass ("java/util/TimeZone"))); - if (!TimeZone_class) { - log_fatal (LOG_DEFAULT, "Fatal error: Could not find java.util.TimeZone class!"); - exit (FATAL_EXIT_MISSING_TIMEZONE_MEMBERS); - } - - TimeZone_getDefault = env->GetStaticMethodID (TimeZone_class, "getDefault", "()Ljava/util/TimeZone;"); - if (!TimeZone_getDefault) { - log_fatal (LOG_DEFAULT, "Fatal error: Could not find java.util.TimeZone.getDefault() method!"); - exit (FATAL_EXIT_MISSING_TIMEZONE_MEMBERS); - } - - TimeZone_getID = env->GetMethodID (TimeZone_class, "getID", "()Ljava/lang/String;"); - if (!TimeZone_getID) { - log_fatal (LOG_DEFAULT, "Fatal error: Could not find java.util.TimeZone.getDefault() method!"); - exit (FATAL_EXIT_MISSING_TIMEZONE_MEMBERS); - } - - /* When running on Android, as per http://developer.android.com/reference/java/lang/System.html#getProperty(java.lang.String) - * the value of java.version is deemed "(Not useful on Android)" and is hardcoded to return zero. We can thus use this fact - * to distinguish between running on a normal JVM and an Android VM. - */ - jclass System_class = env->FindClass ("java/lang/System"); - jmethodID System_getProperty = env->GetStaticMethodID (System_class, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"); - jstring System_javaVersionArg = env->NewStringUTF ("java.version"); - jstring System_javaVersion = reinterpret_cast (env->CallStaticObjectMethod (System_class, System_getProperty, System_javaVersionArg)); - const char* javaVersion = env->GetStringUTFChars (System_javaVersion, NULL); - is_running_on_desktop = atoi (javaVersion) != 0; - env->ReleaseStringUTFChars (System_javaVersion, javaVersion); - env->DeleteLocalRef (System_javaVersionArg); - env->DeleteLocalRef (System_javaVersion); - env->DeleteLocalRef (System_class); return JNI_VERSION_1_6; } @@ -762,39 +716,6 @@ parse_runtime_args (char *runtime_args, RuntimeOptions *options) } #endif // def DEBUG -static void -load_assembly (MonoDomain *domain, JNIEnv *env, jstring assembly) -{ - timing_period total_time; - if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) - total_time.mark_start (); - - const char *assm_name; - MonoAssemblyName *aname; - - assm_name = env->GetStringUTFChars (assembly, NULL); - aname = monoFunctions.assembly_name_new (assm_name); - env->ReleaseStringUTFChars (assembly, assm_name); - - if (domain != monoFunctions.domain_get ()) { - MonoDomain *current = monoFunctions.domain_get (); - monoFunctions.domain_set (domain, FALSE); - monoFunctions.assembly_load_full (aname, NULL, NULL, 0); - monoFunctions.domain_set (current, FALSE); - } else { - monoFunctions.assembly_load_full (aname, NULL, NULL, 0); - } - - monoFunctions.assembly_name_free (aname); - - if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { - total_time.mark_end (); - - timing_diff diff (total_time); - log_info (LOG_TIMING, "Assembly load: %s loaded; elapsed: %lis.%03llu::%llu", assm_name, diff.sec, diff.ms, diff.ns); - } -} - static void set_debug_options (void) { @@ -806,11 +727,11 @@ set_debug_options (void) } #ifdef ANDROID +#ifdef DEBUG static const char *soft_breakpoint_kernel_list[] = { "2.6.32.21-g1e30168", NULL }; -#ifdef DEBUG static int enable_soft_breakpoints (void) { @@ -1023,34 +944,8 @@ mono_runtime_init (char *runtime_args) #endif } -static int -GetAndroidSdkVersion (JNIEnv *env, jobject loader) -{ - jclass lrefVersion = env->FindClass ("android/os/Build$VERSION"); - if (lrefVersion == NULL) { - // Try to load the class from the loader instead. - // Needed by Android designer that uses dynamic loaders - env->ExceptionClear (); - jclass classLoader = env->FindClass ("java/lang/ClassLoader"); - jmethodID classLoader_loadClass = env->GetMethodID (classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); - //env->ExceptionDescribe (); - jstring versionClassName = env->NewStringUTF ("android.os.Build$VERSION"); - - lrefVersion = (jclass)env->CallObjectMethod (loader, classLoader_loadClass, versionClassName); - - env->DeleteLocalRef (classLoader); - env->DeleteLocalRef (versionClassName); - } - jfieldID SDK_INT = env->GetStaticFieldID (lrefVersion, "SDK_INT", "I"); - int version = env->GetStaticIntField (lrefVersion, SDK_INT); - - env->DeleteLocalRef (lrefVersion); - - return version; -} - static MonoDomain* -create_domain (JNIEnv *env, jobjectArray runtimeApks, jstring assembly, jobject loader, mono_bool is_root_domain) +create_domain (JNIEnv *env, jclass runtimeClass, jstring_array_wrapper &runtimeApks, jstring assembly, jobject loader, bool is_root_domain) { MonoDomain *domain; int user_assemblies_count = 0;; @@ -1068,7 +963,7 @@ create_domain (JNIEnv *env, jobjectArray runtimeApks, jstring assembly, jobject domain = monoFunctions.jit_init_version (const_cast ("RootDomain"), const_cast ("mobile")); } else { MonoDomain* root_domain = monoFunctions.get_root_domain (); - char *domain_name = utils.monodroid_strdup_printf ("MonoAndroidDomain%d", GetAndroidSdkVersion (env, loader)); + char *domain_name = utils.monodroid_strdup_printf ("MonoAndroidDomain%d", android_api_level); domain = utils.monodroid_create_appdomain (root_domain, domain_name, /*shadow_copy:*/ 1, /*shadow_directory:*/ androidSystem.get_override_dir (0)); free (domain_name); } @@ -1078,7 +973,7 @@ create_domain (JNIEnv *env, jobjectArray runtimeApks, jstring assembly, jobject // tell the IDE that the project likely need to be recompiled. char* corlib_error_message = monoFunctions.check_corlib_version (); if (corlib_error_message == NULL) { - if (!monodroid_get_system_property ("xamarin.studio.fakefaultycorliberrormessage", &corlib_error_message)) { + if (!androidSystem.monodroid_get_system_property ("xamarin.studio.fakefaultycorliberrormessage", &corlib_error_message)) { free (corlib_error_message); corlib_error_message = NULL; } @@ -1095,40 +990,22 @@ create_domain (JNIEnv *env, jobjectArray runtimeApks, jstring assembly, jobject MonoAssemblyName *aname = monoFunctions.assembly_name_new ("System"); monoFunctions.assembly_load_full (aname, NULL, NULL, 0); monoFunctions.assembly_name_free (aname); - } else { - // Inflate environment from user app assembly - load_assembly (domain, env, assembly); } return domain; } -static void -load_assemblies (MonoDomain *domain, JNIEnv *env, jobjectArray assemblies) -{ - jsize i; - jsize assembliesLength = env->GetArrayLength (assemblies); - /* skip element 0, as that's loaded in create_domain() */ - for (i = 1; i < assembliesLength; ++i) { - jstring assembly = reinterpret_cast (env->GetObjectArrayElement (assemblies, i)); - load_assembly (domain, env, assembly); - env->DeleteLocalRef (assembly); - } -} - static jclass System; static jmethodID System_identityHashCode; static int -LocalRefsAreIndirect (JNIEnv *env, int version) +LocalRefsAreIndirect (JNIEnv *env, jclass runtimeClass, int version) { if (version < 14) return 0; - System = reinterpret_cast (env->NewGlobalRef (env->FindClass ("java/lang/System"))); - - System_identityHashCode = env->GetStaticMethodID (System, - "identityHashCode", "(Ljava/lang/Object;)I"); + System = utils.get_class_from_runtime_field(env, runtimeClass, "java_lang_System", true); + System_identityHashCode = env->GetStaticMethodID (System, "identityHashCode", "(Ljava/lang/Object;)I"); return 1; } @@ -1143,12 +1020,13 @@ _monodroid_get_identity_hash_code (JNIEnv *env, void *v) MONO_API void* _monodroid_timezone_get_default_id (void) { - JNIEnv *env = osBridge.ensure_jnienv (); - jobject d = env->CallStaticObjectMethod (TimeZone_class, TimeZone_getDefault); - jstring id = reinterpret_cast (env->CallObjectMethod (d, TimeZone_getID)); - const char *mutf8 = env->GetStringUTFChars (id, NULL); - - char *def_id = utils.monodroid_strdup_printf ("%s", mutf8); + JNIEnv *env = osBridge.ensure_jnienv (); + jmethodID getDefault = env->GetStaticMethodID (TimeZone_class, "getDefault", "()Ljava/util/TimeZone;"); + jmethodID getID = env->GetMethodID (TimeZone_class, "getID", "()Ljava/lang/String;"); + jobject d = env->CallStaticObjectMethod (TimeZone_class, getDefault); + jstring id = reinterpret_cast (env->CallObjectMethod (d, getID)); + const char *mutf8 = env->GetStringUTFChars (id, NULL); + char *def_id = utils.monodroid_strdup_printf ("%s", mutf8); env->ReleaseStringUTFChars (id, mutf8); env->DeleteLocalRef (id); @@ -1202,6 +1080,23 @@ _monodroid_get_display_dpi (float *x_dpi, float *y_dpi) return -1; } + MonoDomain *domain = nullptr; + if (!runtime_GetDisplayDPI) { + domain = monoFunctions.get_root_domain (); + MonoAssembly *assm = utils.monodroid_load_assembly (domain, "Mono.Android");; + + MonoImage *image = nullptr; + if (assm != nullptr) + image = monoFunctions.assembly_get_image (assm); + + MonoClass *environment = nullptr; + if (image != nullptr) + environment = utils.monodroid_get_class_from_image (domain, image, "Android.Runtime", "AndroidEnvironment"); + + if (environment != nullptr) + runtime_GetDisplayDPI = monoFunctions.class_get_method_from_name (environment, "GetDisplayDPI", 2); + } + if (!runtime_GetDisplayDPI) { *x_dpi = DEFAULT_X_DPI; *y_dpi = DEFAULT_Y_DPI; @@ -1210,7 +1105,7 @@ _monodroid_get_display_dpi (float *x_dpi, float *y_dpi) args [0] = x_dpi; args [1] = y_dpi; - utils.monodroid_runtime_invoke (monoFunctions.get_root_domain (), runtime_GetDisplayDPI, NULL, args, &exc); + utils.monodroid_runtime_invoke (domain != nullptr ? domain : monoFunctions.get_root_domain (), runtime_GetDisplayDPI, NULL, args, &exc); if (exc) { *x_dpi = DEFAULT_X_DPI; *y_dpi = DEFAULT_Y_DPI; @@ -1230,15 +1125,13 @@ lookup_bridge_info (MonoDomain *domain, MonoImage *image, const OSBridge::MonoJa } static void -init_android_runtime (MonoDomain *domain, JNIEnv *env, jobject loader) +init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass runtimeClass, jobject loader) { MonoAssembly *assm; MonoClass *runtime; - MonoClass *environment; MonoImage *image; MonoMethod *method; jclass lrefLoaderClass; - jobject lrefIGCUserPeer; int i; struct JnienvInitializeArgs init = {}; @@ -1250,7 +1143,7 @@ init_android_runtime (MonoDomain *domain, JNIEnv *env, jobject loader) init.logCategories = log_categories; init.version = env->GetVersion (); init.androidSdkVersion = android_api_level; - init.localRefsAreIndirect = LocalRefsAreIndirect (env, init.androidSdkVersion); + init.localRefsAreIndirect = LocalRefsAreIndirect (env, runtimeClass, init.androidSdkVersion); init.isRunningOnDesktop = is_running_on_desktop; // GC threshold is 90% of the max GREF count @@ -1258,11 +1151,9 @@ init_android_runtime (MonoDomain *domain, JNIEnv *env, jobject loader) log_warn (LOG_GC, "GREF GC Threshold: %i", init.grefGcThreshold); - jclass lrefClass = env->FindClass ("java/lang/Class"); - init.grefClass = reinterpret_cast (env->NewGlobalRef (lrefClass)); - init.Class_getName = env->GetMethodID (lrefClass, "getName", "()Ljava/lang/String;"); - init.Class_forName = env->GetStaticMethodID (lrefClass, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"); - env->DeleteLocalRef (lrefClass); + init.grefClass = utils.get_class_from_runtime_field (env, runtimeClass, "java_lang_Class", true); + init.Class_getName = env->GetMethodID (init.grefClass, "getName", "()Ljava/lang/String;"); + init.Class_forName = env->GetStaticMethodID (init.grefClass, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;"); assm = utils.monodroid_load_assembly (domain, "Mono.Android"); image = monoFunctions.assembly_get_image (assm); @@ -1273,7 +1164,6 @@ init_android_runtime (MonoDomain *domain, JNIEnv *env, jobject loader) runtime = utils.monodroid_get_class_from_image (domain, image, "Android.Runtime", "JNIEnv"); method = monoFunctions.class_get_method_from_name (runtime, "Initialize", 1); - environment = utils.monodroid_get_class_from_image (domain, image, "Android.Runtime", "AndroidEnvironment"); if (method == 0) { log_fatal (LOG_DEFAULT, "INTERNAL ERROR: Unable to find Android.Runtime.JNIEnv.Initialize!"); @@ -1291,7 +1181,6 @@ init_android_runtime (MonoDomain *domain, JNIEnv *env, jobject loader) } MonoClass *android_runtime_jnienv = runtime; MonoClassField *bridge_processing_field = monoFunctions.class_get_field_from_name (runtime, const_cast ("BridgeProcessing")); - runtime_GetDisplayDPI = monoFunctions.class_get_method_from_name (environment, "GetDisplayDPI", 2); if (!android_runtime_jnienv || !bridge_processing_field) { log_fatal (LOG_DEFAULT, "INTERNAL_ERROR: Unable to find Android.Runtime.JNIEnv.BridgeProcessing"); exit (FATAL_EXIT_CANNOT_FIND_JNIENV); @@ -1303,13 +1192,11 @@ init_android_runtime (MonoDomain *domain, JNIEnv *env, jobject loader) init.grefLoader = env->NewGlobalRef (loader); - lrefIGCUserPeer = env->FindClass ("mono/android/IGCUserPeer"); - init.grefIGCUserPeer = env->NewGlobalRef (lrefIGCUserPeer); - env->DeleteLocalRef (lrefIGCUserPeer); + init.grefIGCUserPeer = utils.get_class_from_runtime_field(env, runtimeClass, "mono_android_IGCUserPeer", true); - osBridge.initialize_on_runtime_init (env); + osBridge.initialize_on_runtime_init (env, runtimeClass); - log_warn (LOG_DEFAULT, "Calling into managed runtime init"); + log_info (LOG_DEFAULT, "Calling into managed runtime init"); timing_period partial_time; if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) @@ -1321,7 +1208,7 @@ init_android_runtime (MonoDomain *domain, JNIEnv *env, jobject loader) partial_time.mark_end (); timing_diff diff (partial_time); - log_info (LOG_TIMING, "Runtime.init: end native-to-managed transition; elapsed: %lis.%03llu::%llu", diff.sec, diff.ms, diff.ns); + log_info_nocheck (LOG_TIMING, "Runtime.init: end native-to-managed transition; elapsed: %lis:%lu::%lu", diff.sec, diff.ms, diff.ns); } } @@ -1357,48 +1244,6 @@ propagate_uncaught_exception (MonoDomain *domain, JNIEnv *env, jobject javaThrea utils.monodroid_runtime_invoke (domain, method, NULL, args, NULL); } -static void -register_packages (MonoDomain *domain, JNIEnv *env, jobjectArray assemblies) -{ - jsize i; - jsize assembliesLength = env->GetArrayLength (assemblies); - for (i = 0; i < assembliesLength; ++i) { - const char *filename; - char *basename; - MonoAssembly *a; - MonoImage *image; - MonoClass *c; - MonoMethod *m; - jstring assembly = reinterpret_cast (env->GetObjectArrayElement (assemblies, i)); - - filename = env->GetStringUTFChars (assembly, NULL); - basename = utils.monodroid_strdup_printf ("%s", filename); - (*strrchr (basename, '.')) = '\0'; - a = monoFunctions.domain_assembly_open (domain, basename); - if (a == NULL) { - log_fatal (LOG_ASSEMBLY, "Could not load assembly '%s' during startup registration.", basename); - log_fatal (LOG_ASSEMBLY, "This might be due to an invalid debug installation."); - log_fatal (LOG_ASSEMBLY, "A common cause is to 'adb install' the app directly instead of doing from the IDE."); - exit (FATAL_EXIT_MISSING_ASSEMBLY); - } - - - free (basename); - env->ReleaseStringUTFChars (assembly, filename); - env->DeleteLocalRef (assembly); - - image = monoFunctions.assembly_get_image (a); - - c = utils.monodroid_get_class_from_image (domain, image, "Java.Interop", "__TypeRegistrations"); - if (c == NULL) - continue; - m = monoFunctions.class_get_method_from_name (c, "RegisterPackages", 0); - if (m == NULL) - continue; - utils.monodroid_runtime_invoke (domain, m, NULL, NULL, NULL); - } -} - #if DEBUG static void setup_gc_logging (void) @@ -1461,8 +1306,8 @@ monodroid_dlopen (const char *name, int flags, char **err, void *user_data) basename = monodroid_strdup_printf ("libaot-%s", basename); h = androidSystem.load_dso_from_any_directories (basename, dl_flags); - if (h != NULL) - log_info (LOG_ASSEMBLY, "Loaded AOT image '%s'", basename); + if (h != NULL && XA_UNLIKELY (utils.should_log (LOG_ASSEMBLY))) + log_info_nocheck (LOG_ASSEMBLY, "Loaded AOT image '%s'", basename); done_and_out: if (!h && err) { @@ -1490,36 +1335,32 @@ monodroid_dlsym (void *handle, const char *name, char **err, void *user_data) } static void -set_environment_variable_for_directory_full (JNIEnv *env, const char *name, jstring value, int createDirectory, int mode ) +set_environment_variable_for_directory (JNIEnv *env, const char *name, jstring_wrapper &value, bool createDirectory, int mode ) { - const char *v; - - v = env->GetStringUTFChars (value, NULL); if (createDirectory) { - int rv = utils.create_directory (v, mode); + int rv = utils.create_directory (value.get_cstr (), mode); if (rv < 0 && errno != EEXIST) log_warn (LOG_DEFAULT, "Failed to create directory for environment variable %s. %s", name, strerror (errno)); } - setenv (name, v, 1); - env->ReleaseStringUTFChars (value, v); + setenv (name, value.get_cstr (), 1); } static void -set_environment_variable_for_directory (JNIEnv *env, const char *name, jstring value) +set_environment_variable_for_directory (JNIEnv *env, const char *name, jstring_wrapper &value) { - set_environment_variable_for_directory_full (env, name, value, 1, DEFAULT_DIRECTORY_MODE); + set_environment_variable_for_directory (env, name, value, true, DEFAULT_DIRECTORY_MODE); } static void -set_environment_variable (JNIEnv *env, const char *name, jstring value) +set_environment_variable (JNIEnv *env, const char *name, jstring_wrapper &value) { - set_environment_variable_for_directory_full (env, name, value, 0, 0); + set_environment_variable_for_directory (env, name, value, false, 0); } static void -create_xdg_directory (const char *home, const char *relativePath, const char *environmentVariableName) +create_xdg_directory (jstring_wrapper& home, const char *relativePath, const char *environmentVariableName) { - char *dir = utils.monodroid_strdup_printf ("%s/%s", home, relativePath); + char *dir = utils.monodroid_strdup_printf ("%s/%s", home.get_cstr (), relativePath); log_info (LOG_DEFAULT, "Creating XDG directory: %s", dir); int rv = utils.create_directory (dir, DEFAULT_DIRECTORY_MODE); if (rv < 0 && errno != EEXIST) @@ -1530,12 +1371,10 @@ create_xdg_directory (const char *home, const char *relativePath, const char *en } static void -create_xdg_directories_and_environment (JNIEnv *env, jstring homeDir) +create_xdg_directories_and_environment (JNIEnv *env, jstring_wrapper &homeDir) { - const char *home = env->GetStringUTFChars (homeDir, NULL); - create_xdg_directory (home, ".local/share", "XDG_DATA_HOME"); - create_xdg_directory (home, ".config", "XDG_CONFIG_HOME"); - env->ReleaseStringUTFChars (homeDir, home); + create_xdg_directory (homeDir, ".local/share", "XDG_DATA_HOME"); + create_xdg_directory (homeDir, ".config", "XDG_CONFIG_HOME"); } #if DEBUG @@ -1657,16 +1496,12 @@ static void set_profile_options (JNIEnv *env) { char *value; - char *output; - char **args, **ptr; - if (utils.monodroid_get_namespaced_system_property (Debug::DEBUG_MONO_PROFILE_PROPERTY, &value) == 0) return; - output = NULL; - - args = utils.monodroid_strsplit (value, ",", -1); - for (ptr = args; ptr && *ptr; ptr++) { + char *output = nullptr; + char **args = utils.monodroid_strsplit (value, ",", -1); + for (char **ptr = args; ptr && *ptr; ptr++) { const char *arg = *ptr; if (!strncmp (arg, "output=", sizeof ("output=")-1)) { const char *p = arg + (sizeof ("output=")-1); @@ -1865,6 +1700,9 @@ This is a hack to set llvm::DisablePrettyStackTrace to true and avoid this sourc static void disable_external_signal_handlers (void) { + if (!androidSystem.is_mono_llvm_enabled ()) + return; + void *llvm = androidSystem.load_dso ("libLLVM.so", RTLD_LAZY, TRUE); if (llvm) { bool *disable_signals = reinterpret_cast (dlsym (llvm, "_ZN4llvm23DisablePrettyStackTraceE")); @@ -1879,11 +1717,10 @@ disable_external_signal_handlers (void) MONO_API void _monodroid_counters_dump (const char *format, ...) { - va_list args; - if (counters == NULL) return; + va_list args; fprintf (counters, "\n"); va_start (args, format); @@ -1902,17 +1739,15 @@ monodroid_Mono_UnhandledException_internal (MonoException *ex) } static MonoDomain* -create_and_initialize_domain (JNIEnv* env, jobjectArray runtimeApks, jobjectArray assemblies, jobject loader, mono_bool is_root_domain) +create_and_initialize_domain (JNIEnv* env, jclass runtimeClass, jstring_array_wrapper &runtimeApks, jobjectArray assemblies, jobject loader, bool is_root_domain) { - MonoDomain* domain = create_domain (env, runtimeApks, reinterpret_cast (env->GetObjectArrayElement (assemblies, 0)), loader, is_root_domain); + MonoDomain* domain = create_domain (env, runtimeClass, runtimeApks, reinterpret_cast (env->GetObjectArrayElement (assemblies, 0)), loader, is_root_domain); // When running on desktop, the root domain is only a dummy so don't initialize it if (is_running_on_desktop && is_root_domain) return domain; - load_assemblies (domain, env, assemblies); - init_android_runtime (domain, env, loader); - register_packages (domain, env, assemblies); + init_android_runtime (domain, env, runtimeClass, loader); osBridge.add_monodroid_domain (domain); @@ -1920,17 +1755,11 @@ create_and_initialize_domain (JNIEnv* env, jobjectArray runtimeApks, jobjectArra } JNIEXPORT void JNICALL -Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApks, jstring runtimeNativeLibDir, jobjectArray appDirs, jobject loader, jobjectArray externalStorageDirs, jobjectArray assemblies, jstring packageName) +Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, + jstring runtimeNativeLibDir, jobjectArray appDirs, jobject loader, + jobjectArray externalStorageDirs, jobjectArray assemblies, jstring packageName, + jint apiLevel, jobjectArray environmentVariables) { - char *runtime_args = NULL; - char *connect_args; - jstring libdir_s; - const char *libdir, *esd; - char *counters_path; - const char *pkgName; - char *aotMode; - int i; - init_logging_categories (); timing_period total_time; @@ -1938,34 +1767,38 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject total_time.mark_start (); } - android_api_level = GetAndroidSdkVersion (env, loader); + android_api_level = apiLevel; - pkgName = env->GetStringUTFChars (packageName, NULL); - utils.monodroid_store_package_name (pkgName); /* Will make a copy of the string */ - env->ReleaseStringUTFChars (packageName, pkgName); + TimeZone_class = utils.get_class_from_runtime_field (env, klass, "java_util_TimeZone", true); - disable_external_signal_handlers (); + jstring_wrapper jstr (env, packageName); + utils.monodroid_store_package_name (jstr.get_cstr ()); + + jstr = lang; + set_environment_variable (env, "LANG", jstr); + + androidSystem.setup_environment (env, environmentVariables); - jstring homeDir = reinterpret_cast (env->GetObjectArrayElement (appDirs, 0)); - set_environment_variable (env, "LANG", lang); - set_environment_variable_for_directory (env, "HOME", homeDir); - set_environment_variable_for_directory (env, "TMPDIR", reinterpret_cast (env->GetObjectArrayElement (appDirs, 1))); - create_xdg_directories_and_environment (env, homeDir); + jstr = reinterpret_cast (env->GetObjectArrayElement (appDirs, 1)); + set_environment_variable_for_directory (env, "TMPDIR", jstr); - androidSystem.setup_environment (env, runtimeApks); + jstr = reinterpret_cast (env->GetObjectArrayElement (appDirs, 0)); + set_environment_variable_for_directory (env, "HOME", jstr); + create_xdg_directories_and_environment (env, jstr); + primary_override_dir = get_primary_override_dir (env, jstr); - if (android_api_level < 23 || getenv ("__XA_DSO_IN_APK") == NULL) { + disable_external_signal_handlers (); + + jstring_array_wrapper runtimeApks (env, runtimeApksJava); + if (android_api_level < 23 || !androidSystem.is_embedded_dso_mode_enabled ()) { log_info (LOG_DEFAULT, "Setting up for DSO lookup in app data directories"); - libdir_s = reinterpret_cast (env->GetObjectArrayElement (appDirs, 2)); - libdir = env->GetStringUTFChars (libdir_s, NULL); + jstr = env->GetObjectArrayElement (appDirs, 2); AndroidSystem::app_lib_directories_size = 1; AndroidSystem::app_lib_directories = (const char**) xcalloc (AndroidSystem::app_lib_directories_size, sizeof(char*)); - AndroidSystem::app_lib_directories [0] = utils.monodroid_strdup_printf ("%s", libdir); - env->ReleaseStringUTFChars (libdir_s, libdir); + AndroidSystem::app_lib_directories [0] = strdup (jstr.get_cstr ()); } else { log_info (LOG_DEFAULT, "Setting up for DSO lookup directly in the APK"); - embedded_dso_mode = 1; - AndroidSystem::app_lib_directories_size = env->GetArrayLength (runtimeApks); + AndroidSystem::app_lib_directories_size = runtimeApks.get_length (); AndroidSystem::app_lib_directories = (const char**) xcalloc (AndroidSystem::app_lib_directories_size, sizeof(char*)); unsigned short built_for_cpu = 0, running_on_cpu = 0; @@ -1974,16 +1807,13 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject androidSystem.setup_apk_directories (env, running_on_cpu, runtimeApks); } - primary_override_dir = get_primary_override_dir (env, reinterpret_cast (env->GetObjectArrayElement (appDirs, 0))); - esd = env->GetStringUTFChars (reinterpret_cast (env->GetObjectArrayElement (externalStorageDirs, 0)), NULL); - external_override_dir = utils.monodroid_strdup_printf ("%s", esd); - env->ReleaseStringUTFChars (reinterpret_cast (env->GetObjectArrayElement (externalStorageDirs, 0)), esd); + jstr = env->GetObjectArrayElement (externalStorageDirs, 0); + external_override_dir = strdup (jstr.get_cstr ()); - esd = env->GetStringUTFChars (reinterpret_cast (env->GetObjectArrayElement (externalStorageDirs, 1)), nullptr); - external_legacy_override_dir = utils.monodroid_strdup_printf ("%s", esd); - env->ReleaseStringUTFChars (reinterpret_cast (env->GetObjectArrayElement (externalStorageDirs, 1)), esd); + jstr = env->GetObjectArrayElement (externalStorageDirs, 1); + external_legacy_override_dir = strdup (jstr.get_cstr ()); - init_reference_logging(primary_override_dir); + init_reference_logging (primary_override_dir); androidSystem.create_update_dir (primary_override_dir); #if DEBUG @@ -1994,7 +1824,7 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject #ifndef RELEASE androidSystem.set_override_dir (1, external_override_dir); androidSystem.set_override_dir (2, external_legacy_override_dir); - for (i = 0; i < AndroidSystem::MAX_OVERRIDES; ++i) { + for (uint32_t i = 0; i < AndroidSystem::MAX_OVERRIDES; ++i) { const char *p = androidSystem.get_override_dir (i); if (!utils.directory_exists (p)) continue; @@ -2004,10 +1834,8 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject setup_bundled_app ("libmonodroid_bundle_app.so"); if (runtimeNativeLibDir != NULL) { - const char *rd; - rd = env->GetStringUTFChars (runtimeNativeLibDir, NULL); - runtime_libdir = utils.monodroid_strdup_printf ("%s", rd); - env->ReleaseStringUTFChars (runtimeNativeLibDir, rd); + jstr = runtimeNativeLibDir; + runtime_libdir = strdup (jstr.get_cstr ()); log_warn (LOG_DEFAULT, "Using runtime path: %s", runtime_libdir); } @@ -2018,7 +1846,7 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject * symbols against the Mono library we're loading. */ int sgen_dlopen_flags = RTLD_LAZY | RTLD_GLOBAL; - if (embedded_dso_mode) { + if (androidSystem.is_embedded_dso_mode_enabled ()) { libmonosgen_handle = androidSystem.load_dso_from_any_directories (AndroidSystem::MONO_SGEN_SO, sgen_dlopen_flags); } @@ -2030,13 +1858,11 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject exit (FATAL_EXIT_CANNOT_FIND_MONO); } androidSystem.setup_process_args (env, runtimeApks); -#ifndef WINDOWS - _monodroid_getifaddrs_init (); -#endif - if ((log_categories & LOG_TIMING) != 0) { + if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) { monoFunctions.counters_enable (XA_LOG_COUNTERS); - counters_path = utils.path_combine (androidSystem.get_override_dir (0), "counters.txt"); + char *counters_path = utils.path_combine (androidSystem.get_override_dir (0), "counters.txt"); + log_info_nocheck (LOG_TIMING, "counters path: %s", counters_path); counters = utils.monodroid_fopen (counters_path, "a"); utils.set_world_accessable (counters_path); free (counters_path); @@ -2048,9 +1874,10 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject set_trace_options (); +#if defined (DEBUG) && !defined (WINDOWS) + char *connect_args; utils.monodroid_get_namespaced_system_property (Debug::DEBUG_MONO_CONNECT_PROPERTY, &connect_args); -#if defined (DEBUG) && !defined (WINDOWS) if (connect_args) { int res = debug.start_connection (connect_args); if (res != 2) { @@ -2072,44 +1899,48 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject monoFunctions.config_parse_memory (reinterpret_cast (monodroid_config)); monoFunctions.register_machine_config (reinterpret_cast (monodroid_machine_config)); - log_info (LOG_DEFAULT, "Probing for mono.aot AOT mode\n"); - - if (monodroid_get_system_property ("mono.aot", &aotMode) > 0) { - MonoAotMode mode = static_cast (0); - if (strcmp (aotMode, "normal") == 0) - mode = MonoAotMode::MONO_AOT_MODE_NORMAL; - else if (strcmp (aotMode, "hybrid") == 0) - mode = MonoAotMode::MONO_AOT_MODE_HYBRID; - else if (strcmp (aotMode, "full") == 0) - mode = MonoAotMode::MONO_AOT_MODE_FULL; - else - log_warn (LOG_DEFAULT, "Unknown mono.aot property value: %s\n", aotMode); - - if (mode != MonoAotMode::MONO_AOT_MODE_NORMAL) { - log_info (LOG_DEFAULT, "Enabling %s AOT mode in Mono\n", aotMode); - monoFunctions.jit_set_aot_mode (mode); - } + log_info (LOG_DEFAULT, "Probing for Mono AOT mode\n"); + + MonoAotMode mode = androidSystem.get_mono_aot_mode (); + if (mode == MonoAotMode::MONO_AOT_MODE_UNKNOWN) + mode = MonoAotMode::MONO_AOT_MODE_NONE; + + if (mode != MonoAotMode::MONO_AOT_MODE_NORMAL && mode != MonoAotMode::MONO_AOT_MODE_NONE) { + log_info (LOG_DEFAULT, "Enabling AOT mode in Mono"); + monoFunctions.jit_set_aot_mode (mode); } log_info (LOG_DEFAULT, "Probing if we should use LLVM\n"); - if (monodroid_get_system_property ("mono.llvm", NULL) > 0) { + if (androidSystem.is_mono_llvm_enabled ()) { char *args [1]; args[0] = const_cast ("--llvm"); - log_info (LOG_DEFAULT, "Found mono.llvm property, enabling LLVM mode in Mono\n"); + log_info (LOG_DEFAULT, "Enabling LLVM mode in Mono\n"); monoFunctions.jit_parse_options (1, args); monoFunctions.set_use_llvm (true); } + char *runtime_args = nullptr; utils.monodroid_get_namespaced_system_property (Debug::DEBUG_MONO_EXTRA_PROPERTY, &runtime_args); #if TRACE __android_log_print (ANDROID_LOG_INFO, "*jonp*", "debug.mono.extra=%s", runtime_args); #endif + timing_period partial_time; + if (XA_UNLIKELY (utils.should_log (LOG_TIMING))) + partial_time.mark_start (); + mono_runtime_init (runtime_args); + 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); + } + /* the first assembly is used to initialize the AppDomain name */ - create_and_initialize_domain (env, runtimeApks, assemblies, loader, /*is_root_domain:*/ 1); + create_and_initialize_domain (env, klass, runtimeApks, assemblies, loader, /*is_root_domain:*/ true); free (runtime_args); @@ -2123,7 +1954,7 @@ Java_mono_android_Runtime_init (JNIEnv *env, jclass klass, jstring lang, jobject total_time.mark_end (); timing_diff diff (total_time); - log_info (LOG_TIMING, "Runtime.init: end, total time; elapsed: %lis.%03llu::%llu", diff.sec, diff.ms, diff.ns); + log_info_nocheck (LOG_TIMING, "Runtime.init: end, total time; elapsed: %lis:%lu::%lu", diff.sec, diff.ms, diff.ns); _monodroid_counters_dump ("## Runtime.init: end"); } } @@ -2170,7 +2001,7 @@ JNICALL Java_mono_android_Runtime_register (JNIEnv *env, jclass klass, jstring m total_time.mark_end (); timing_diff diff (total_time); - log_info (LOG_TIMING, "Runtime.register: end time; elapsed: %lis.%03llu::%llu", diff.sec, diff.ms, diff.ns); + log_info_nocheck (LOG_TIMING, "Runtime.register: end time; elapsed: %lis:%lu::%lu", diff.sec, diff.ms, diff.ns); _monodroid_counters_dump ("## Runtime.register: type=%s\n", type); } @@ -2192,13 +2023,15 @@ reinitialize_android_runtime_type_manager (JNIEnv *env) } JNIEXPORT jint -JNICALL Java_mono_android_Runtime_createNewContext (JNIEnv *env, jclass klass, jobjectArray runtimeApks, jobjectArray assemblies, jobject loader) +JNICALL Java_mono_android_Runtime_createNewContext (JNIEnv *env, jclass klass, jobjectArray runtimeApksJava, jobjectArray assemblies, jobject loader) { log_info (LOG_DEFAULT, "CREATING NEW CONTEXT"); reinitialize_android_runtime_type_manager (env); MonoDomain *root_domain = monoFunctions.get_root_domain (); monoFunctions.jit_thread_attach (root_domain); - MonoDomain *domain = create_and_initialize_domain (env, runtimeApks, assemblies, loader, /*is_root_domain:*/ 0); + + jstring_array_wrapper runtimeApks (env, runtimeApksJava); + MonoDomain *domain = create_and_initialize_domain (env, klass, runtimeApks, assemblies, loader, /*is_root_domain:*/ false); monoFunctions.domain_set (domain, FALSE); int domain_id = monoFunctions.domain_get_id (domain); current_context_id = domain_id; diff --git a/src/monodroid/jni/monodroid.h b/src/monodroid/jni/monodroid.h index c80f72408e1..5d44a06034f 100644 --- a/src/monodroid/jni/monodroid.h +++ b/src/monodroid/jni/monodroid.h @@ -25,7 +25,13 @@ #ifdef __cplusplus #define MONO_API extern "C" MONO_API_DEF #else -#define MONO_API MONO_API_DEF + +/* Use our own definition, to stay consistent */ +#if defined (MONO_API) +#undef MONO_API #endif +#define MONO_API MONO_API_DEF + +#endif /* __cplusplus */ #endif /* defined __MONODROID_H */ diff --git a/src/monodroid/jni/new_delete.cc b/src/monodroid/jni/new_delete.cc new file mode 100644 index 00000000000..e7e68e24be8 --- /dev/null +++ b/src/monodroid/jni/new_delete.cc @@ -0,0 +1,79 @@ +#include +#include + +extern "C" { +#include "java-interop-util.h" +} + +static void* +do_alloc (size_t size) +{ + return ::malloc (size == 0 ? 1 : size); +} + +void* +operator new (size_t size) +{ + void* p = do_alloc (size); + if (p == nullptr) { + log_fatal (LOG_DEFAULT, "Out of memory in the `new` operator"); + exit (FATAL_EXIT_OUT_OF_MEMORY); + } + + return p; +} + +void* +operator new (size_t size, const std::nothrow_t&) noexcept +{ + return do_alloc (size); +} + +void* +operator new[] (size_t size) +{ + return ::operator new (size); +} + +void* +operator new[] (size_t size, const std::nothrow_t&) noexcept +{ + return do_alloc (size); +} + +void +operator delete (void* ptr) noexcept +{ + if (ptr) + ::free (ptr); +} + +void +operator delete (void* ptr, const std::nothrow_t&) noexcept +{ + ::operator delete (ptr); +} + +void +operator delete (void* ptr, size_t) noexcept +{ + ::operator delete (ptr); +} + +void +operator delete[] (void* ptr) noexcept +{ + ::operator delete (ptr); +} + +void +operator delete[] (void* ptr, const std::nothrow_t&) noexcept +{ + ::operator delete[] (ptr); +} + +void +operator delete[] (void* ptr, size_t) noexcept +{ + ::operator delete[] (ptr); +} diff --git a/src/monodroid/jni/osbridge.cc b/src/monodroid/jni/osbridge.cc index 0530abbc8c6..4d13913de1a 100644 --- a/src/monodroid/jni/osbridge.cc +++ b/src/monodroid/jni/osbridge.cc @@ -1,5 +1,5 @@ -#include -#include +#include +#include #include #if defined (LINUX) || defined (__linux__) || defined (__linux) @@ -927,8 +927,10 @@ OSBridge::gc_cross_references (int num_sccs, MonoGCBridgeSCC **sccs, int num_xre } } - for (i = 0; i < num_xrefs; ++i) - log_info (LOG_GC, "xref [%d] %d -> %d", i, xrefs [i].src_scc_index, xrefs [i].dst_scc_index); + if (utils.should_log (LOG_GC)) { + for (i = 0; i < num_xrefs; ++i) + log_info_nocheck (LOG_GC, "xref [%d] %d -> %d", i, xrefs [i].src_scc_index, xrefs [i].dst_scc_index); + } } #endif @@ -1062,10 +1064,10 @@ OSBridge::initialize_on_onload (JavaVM *vm, JNIEnv *env) } void -OSBridge::initialize_on_runtime_init (JNIEnv *env) +OSBridge::initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) { assert (env != nullptr); - GCUserPeer_class = reinterpret_cast (lref_to_gref (env, env->FindClass ("mono/android/GCUserPeer"))); + GCUserPeer_class = utils.get_class_from_runtime_field(env, runtimeClass, "mono_android_GCUserPeer", true); GCUserPeer_ctor = env->GetMethodID (GCUserPeer_class, "", "()V"); assert ( (GCUserPeer_class && GCUserPeer_ctor) || !"Failed to load mono.android.GCUserPeer!" ); } diff --git a/src/monodroid/jni/osbridge.h b/src/monodroid/jni/osbridge.h index a7914b4fb3c..ef82970a72f 100644 --- a/src/monodroid/jni/osbridge.h +++ b/src/monodroid/jni/osbridge.h @@ -107,7 +107,7 @@ namespace xamarin { namespace android { namespace internal int get_gref_gc_threshold (); JNIEnv* ensure_jnienv (); void initialize_on_onload (JavaVM *vm, JNIEnv *env); - void initialize_on_runtime_init (JNIEnv *env); + void initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass); void add_monodroid_domain (MonoDomain *domain); void remove_monodroid_domain (MonoDomain *domain); void on_destroy_contexts (); diff --git a/src/monodroid/jni/util.cc b/src/monodroid/jni/util.cc index 36798593a5f..bee3add5faf 100644 --- a/src/monodroid/jni/util.cc +++ b/src/monodroid/jni/util.cc @@ -34,6 +34,8 @@ using timestruct = timespec; using timestruct = timeval; #endif +static const char hex_chars [] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + void timing_point::mark () { int ret; @@ -246,14 +248,52 @@ Util::recv_uninterrupted (int fd, void *buf, int len) return total; } +#if WINDOWS +// +// This version should be removed once MXE we have on mac can build the glorious version in the +// #else below. +// +// Currently mxe fails with: +// +// Cannot export _ZN7xamarin7android4Util19package_hash_to_hexIiIEEEvjT_DpT0_: symbol wrong type (4 vs 3) +// Cannot export _ZN7xamarin7android4Util19package_hash_to_hexIiIiEEEvjT_DpT0_: symbol wrong type (4 vs 3) +// Cannot export _ZN7xamarin7android4Util19package_hash_to_hexIiIiiEEEvjT_DpT0_: symbol wrong type (4 vs 3) +// Cannot export _ZN7xamarin7android4Util19package_hash_to_hexIiIiiiEEEvjT_DpT0_: symbol wrong type (4 vs 3) +// Cannot export _ZN7xamarin7android4Util19package_hash_to_hexIiIiiiiEEEvjT_DpT0_: symbol wrong type (4 vs 3) +// Cannot export _ZN7xamarin7android4Util19package_hash_to_hexIiIiiiiiEEEvjT_DpT0_: symbol wrong type (4 vs 3) +// Cannot export _ZN7xamarin7android4Util19package_hash_to_hexIiIiiiiiiEEEvjT_DpT0_: symbol wrong type (4 vs 3) +// Cannot export _ZN7xamarin7android4Util19package_hash_to_hexIiIiiiiiiiEEEvjT_DpT0_: symbol wrong type (4 vs 3) +// collect2 : error : ld returned 1 exit status +// [/Users/builder/jenkins/workspace/xamarin-android-pr-builder-debug/xamarin-android/src/monodroid/monodroid.csproj] +// +void Util::package_hash_to_hex (uint32_t hash) +{ + for (uint32_t idx = 0; idx < 8; idx++) { + package_property_suffix [idx] = hex_chars [(hash & (0xF0000000 >> idx * 4)) >> ((7 - idx) * 4)]; + } + package_property_suffix[sizeof (package_property_suffix) / sizeof (char) - 1] = 0x00; +} +#else +template +inline void +Util::package_hash_to_hex (IdxType /* idx */) +{ + package_property_suffix[sizeof (package_property_suffix) / sizeof (char) - 1] = 0x00; +} + +template +inline void +Util::package_hash_to_hex (uint32_t hash, IdxType idx, Indices... indices) +{ + package_property_suffix [idx] = hex_chars [(hash & (0xF0000000 >> idx * 4)) >> ((7 - idx) * 4)]; + package_hash_to_hex (hash, indices...); +} +#endif + void Util::monodroid_store_package_name (const char *name) { - const char *ch; - int hash; - - memset (package_property_suffix, 0, sizeof (package_property_suffix)); - if (!name || strlen (name) == 0) + if (!name || *name == '\0') return; /* Android properties can be at most 32 bytes long (!) and so we mustn't append the package name @@ -262,11 +302,21 @@ Util::monodroid_store_package_name (const char *name) * as a stream of bytes assumming it's an ASCII string using a simplified version of the hash * algorithm used by BCL's String.GetHashCode () */ - ch = name; - hash = 0; + const char *ch = name; + uint32_t hash = 0; while (*ch) hash = (hash << 5) - (hash + *ch++); - snprintf (package_property_suffix, sizeof (package_property_suffix), "%08x", hash); + +#if WINDOWS + package_hash_to_hex (hash); +#else + // In C++14 or newer we could use std::index_sequence, but in C++11 it's a bit too much ado + // for this simple case, so a manual sequence it is. + // + // And yes, I know it could be done in a simple loop or in even simpler 8 lines of code, but + // that would be boring, wouldn't it? :) + package_hash_to_hex (hash, 0, 1, 2, 3, 4, 5, 6, 7); +#endif log_info (LOG_DEFAULT, "Generated hash 0x%s for package name %s", package_property_suffix, name); } @@ -283,13 +333,13 @@ Util::monodroid_get_namespaced_system_property (const char *name, char **value) log_info (LOG_DEFAULT, "Trying to get property %s.%s", name, package_property_suffix); char *propname = monodroid_strdup_printf ("%s.%s", name, package_property_suffix); if (propname) { - result = monodroid_get_system_property (propname, &local_value); + result = androidSystem.monodroid_get_system_property (propname, &local_value); free (propname); } } if (result <= 0 || !local_value) - result = monodroid_get_system_property (name, &local_value); + result = androidSystem.monodroid_get_system_property (name, &local_value); if (result > 0) { if (strlen (local_value) == 0) { @@ -642,6 +692,22 @@ Util::is_path_rooted (const char *path) #endif } +jclass +Util::get_class_from_runtime_field (JNIEnv *env, jclass runtime, const char *name, bool make_gref) +{ + static constexpr char java_lang_class_sig[] = "Ljava/lang/Class;"; + + jfieldID fieldID = env->GetStaticFieldID (runtime, name, java_lang_class_sig); + if (fieldID == nullptr) + return nullptr; + + jobject field = env->GetStaticObjectField (runtime, fieldID); + if (field == nullptr) + return nullptr; + + return reinterpret_cast (make_gref ? osBridge.lref_to_gref (env, field) : field); +} + extern "C" void monodroid_strfreev (char **str_array) { diff --git a/src/monodroid/jni/util.h b/src/monodroid/jni/util.h index 2a82a2d7078..e6dbc1ca9b8 100644 --- a/src/monodroid/jni/util.h +++ b/src/monodroid/jni/util.h @@ -43,9 +43,11 @@ typedef struct dirent monodroid_dirent_t; #include #include #include +#include #include "monodroid.h" #include "dylib-mono.h" +#include "jni-wrappers.h" #ifdef __cplusplus extern "C" { @@ -83,8 +85,8 @@ namespace xamarin { namespace android { struct timing_point { - time_t sec = 0; - uint64_t ns = 0; + time_t sec; + uint64_t ns; void mark (); }; @@ -118,10 +120,12 @@ namespace xamarin { namespace android class Util { - public: - explicit Util () - : package_property_suffix {0} - {} +#if defined (ANDROID) || defined (LINUX) + using timestruct = timespec; +#else + using timestruct = timeval; +#endif + static constexpr uint32_t ms_in_nsec = 1000000ULL; public: FILE *monodroid_fopen (const char* filename, const char* mode); @@ -151,6 +155,8 @@ namespace xamarin { namespace android bool file_exists (const char *file); bool directory_exists (const char *directory); bool file_copy (const char *to, const char *from); + jclass get_class_from_runtime_field (JNIEnv *env, jclass runtime, const char *name, bool make_gref = false); + #ifdef WINDOWS /* Those two conversion functions are only properly implemented on Windows * because that's the only place where they should be useful. @@ -192,6 +198,16 @@ namespace xamarin { namespace android void add_to_vector (char ***vector, int size, char *token); void monodroid_property_set (MonoDomain *domain, MonoProperty *property, void *obj, void **params, MonoObject **exc); +#if WINDOWS + void package_hash_to_hex (uint32_t hash); +#else + template + void package_hash_to_hex (IdxType idx); + + template + void package_hash_to_hex (uint32_t hash, IdxType idx, Indices... indices); +#endif + int make_directory (const char *path, int mode) { #if WINDOWS diff --git a/src/monodroid/jni/xamarin_getifaddrs.cc b/src/monodroid/jni/xamarin_getifaddrs.cc index 684f67ad882..66e349b2696 100644 --- a/src/monodroid/jni/xamarin_getifaddrs.cc +++ b/src/monodroid/jni/xamarin_getifaddrs.cc @@ -26,6 +26,7 @@ extern "C" { #include "logger.h" } +#include "globals.h" #include "xamarin_getifaddrs.h" /* Some of these aren't defined in android's rtnetlink.h (as of ndk 16). We define values for all of @@ -269,6 +270,8 @@ typedef void (*freeifaddrs_impl_fptr)(struct _monodroid_ifaddrs *ifa); static getifaddrs_impl_fptr getifaddrs_impl = NULL; static freeifaddrs_impl_fptr freeifaddrs_impl = NULL; +static bool initialized; +static std::mutex init_lock; void _monodroid_getifaddrs_init () @@ -279,6 +282,14 @@ _monodroid_getifaddrs_init () int _monodroid_getifaddrs (struct _monodroid_ifaddrs **ifap) { + if (!initialized) { + std::lock_guard lock (init_lock); + if (!initialized) { + _monodroid_getifaddrs_init (); + initialized = true; + } + } + int ret = -1; if (getifaddrs_impl) @@ -361,10 +372,11 @@ get_ifaddrs_impl (int (**getifaddrs_impl) (struct _monodroid_ifaddrs **ifap), vo *freeifaddrs_impl = reinterpret_cast (dlsym (libc, "freeifaddrs")); } - if (!*getifaddrs_impl) + if (!*getifaddrs_impl) { log_info (LOG_NET, "This libc does not have getifaddrs/freeifaddrs, using Xamarin's\n"); - else + } else { log_info (LOG_NET, "This libc has getifaddrs/freeifaddrs\n"); + } } static void @@ -540,20 +552,22 @@ parse_netlink_reply (netlink_session *session, struct _monodroid_ifaddrs **ifadd } #if DEBUG - log_debug (LOG_NETLINK, "response flags:"); - if (netlink_reply.msg_flags == 0) - log_debug (LOG_NETLINK, " [NONE]"); - else { - if (netlink_reply.msg_flags & MSG_EOR) - log_debug (LOG_NETLINK, " MSG_EOR"); - if (netlink_reply.msg_flags & MSG_TRUNC) - log_debug (LOG_NETLINK, " MSG_TRUNC"); - if (netlink_reply.msg_flags & MSG_CTRUNC) - log_debug (LOG_NETLINK, " MSG_CTRUNC"); - if (netlink_reply.msg_flags & MSG_OOB) - log_debug (LOG_NETLINK, " MSG_OOB"); - if (netlink_reply.msg_flags & MSG_ERRQUEUE) - log_debug (LOG_NETLINK, " MSG_ERRQUEUE"); + if (utils.should_log (LOG_NETLINK)) { + log_debug_nocheck (LOG_NETLINK, "response flags:"); + if (netlink_reply.msg_flags == 0) + log_debug_nocheck (LOG_NETLINK, " [NONE]"); + else { + if (netlink_reply.msg_flags & MSG_EOR) + log_debug_nocheck (LOG_NETLINK, " MSG_EOR"); + if (netlink_reply.msg_flags & MSG_TRUNC) + log_debug_nocheck (LOG_NETLINK, " MSG_TRUNC"); + if (netlink_reply.msg_flags & MSG_CTRUNC) + log_debug_nocheck (LOG_NETLINK, " MSG_CTRUNC"); + if (netlink_reply.msg_flags & MSG_OOB) + log_debug_nocheck (LOG_NETLINK, " MSG_OOB"); + if (netlink_reply.msg_flags & MSG_ERRQUEUE) + log_debug_nocheck (LOG_NETLINK, " MSG_ERRQUEUE"); + } } #endif @@ -784,11 +798,13 @@ calculate_address_netmask (struct _monodroid_ifaddrs *ifa, struct ifaddrmsg *net if (prefix_bytes + 2 < data_length) /* Set the rest of the mask bits in the byte following the last 0xFF value */ netmask_data [prefix_bytes + 1] = 0xff << (8 - (prefix_length % 8)); - log_debug (LOG_NETLINK, " netmask is: "); - for (i = 0; i < data_length; i++) { - log_debug (LOG_NETLINK, "%s%u", i == 0 ? " " : ".", (unsigned char)ifa->ifa_netmask->sa_data [i]); + if (utils.should_log (LOG_NETLINK)) { + log_debug_nocheck (LOG_NETLINK, " netmask is: "); + for (i = 0; i < data_length; i++) { + log_debug_nocheck (LOG_NETLINK, "%s%u", i == 0 ? " " : ".", (unsigned char)ifa->ifa_netmask->sa_data [i]); + } + log_debug_nocheck (LOG_NETLINK, "\n"); } - log_debug (LOG_NETLINK, "\n"); } } @@ -975,8 +991,10 @@ get_link_info (const struct nlmsghdr *message) if (!ifa->ifa_name) { goto error; } - log_debug (LOG_NETLINK, " interface name (payload length: %d; string length: %d)\n", RTA_PAYLOAD (attribute), strlen (ifa->ifa_name)); - log_debug (LOG_NETLINK, " %s\n", ifa->ifa_name); + if (utils.should_log (LOG_NETLINK)) { + log_debug_nocheck (LOG_NETLINK, " interface name (payload length: %d; string length: %d)\n", RTA_PAYLOAD (attribute), strlen (ifa->ifa_name)); + log_debug_nocheck (LOG_NETLINK, " %s\n", ifa->ifa_name); + } break; case IFLA_BROADCAST: @@ -1094,10 +1112,13 @@ struct enumvalue iflas[] = { static void print_ifla_name (int id) { + if (!utils.should_log (LOG_NETLINK)) + return; + int i = 0; while (1) { if (iflas [i].value == -1 && iflas [i].name == 0) { - log_info (LOG_NETLINK, "Unknown ifla->name: unknown id %d\n", id); + log_info_nocheck (LOG_NETLINK, "Unknown ifla->name: unknown id %d\n", id); break; } @@ -1105,7 +1126,7 @@ print_ifla_name (int id) i++; continue; } - log_info (LOG_NETLINK, "ifla->name: %s (%d)\n", iflas [i].name, iflas [i].value); + log_info_nocheck (LOG_NETLINK, "ifla->name: %s (%d)\n", iflas [i].name, iflas [i].value); break; } } @@ -1113,11 +1134,14 @@ print_ifla_name (int id) static void print_address_list (const char title[], struct _monodroid_ifaddrs *list) { + if (!utils.should_log (LOG_NETLINK)) + return; + struct _monodroid_ifaddrs *cur; char *msg, *tmp; if (!list) { - log_info (LOG_NETLINK, "monodroid-net", "No list to print in %s", __FUNCTION__); + log_info_nocheck (LOG_NETLINK, "monodroid-net", "No list to print in %s", __FUNCTION__); return; } @@ -1132,7 +1156,7 @@ print_address_list (const char title[], struct _monodroid_ifaddrs *list) cur = cur->ifa_next; } - log_info (LOG_NETLINK, "%s: %s", title, msg ? msg : "[no addresses]"); + log_info_nocheck (LOG_NETLINK, "%s: %s", title, msg ? msg : "[no addresses]"); free (msg); } #endif diff --git a/src/monodroid/monodroid.targets b/src/monodroid/monodroid.targets index 366b643152e..484f5a3a1a0 100644 --- a/src/monodroid/monodroid.targets +++ b/src/monodroid/monodroid.targets @@ -26,7 +26,7 @@