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 759d1a9accb..4ea64b85fb8 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 @@