Skip to content

Commit

Permalink
[Mono.Android] fix leaking lrefs in TypeManager (dotnet#9136)
Browse files Browse the repository at this point in the history
Context: dotnet/maui#23694 (review)
Context: https://github.com/dotnet/maui/blob/d38ca872f68326ab623c050b0efd93c7d212e000/src/Essentials/test/DeviceTests/Tests/Preferences_Tests.cs#L305-L310
Context: dotnet/android@06bb1dc...45855b8

A .NET MAUI on-device test is crashing on API 23 emulators:

    07-23 11:35:45.837  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115] JNI ERROR (app bug): local reference table overflow (max=512)
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115] local reference table dump:
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]   Last 10 entries (of 512):
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]       511: 0x6ff7e140 java.lang.Class<android.app.SharedPreferencesImpl>
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]       510: 0x6ff7db18 java.lang.Class<android.app.SharedPreferencesImpl$EditorImpl>
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]       509: 0x6fed5750 java.lang.Class<android.content.SharedPreferences$Editor>
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]       508: 0x6ff7e140 java.lang.Class<android.app.SharedPreferencesImpl>
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]       507: 0x6fed57d8 java.lang.Class<android.content.SharedPreferences>
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]       506: 0x6ff7e140 java.lang.Class<android.app.SharedPreferencesImpl>
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]       505: 0x6fed57d8 java.lang.Class<android.content.SharedPreferences>
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]       504: 0x6ff7db18 java.lang.Class<android.app.SharedPreferencesImpl$EditorImpl>
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]       503: 0x6fed5750 java.lang.Class<android.content.SharedPreferences$Editor>
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]       502: 0x6ff7e140 java.lang.Class<android.app.SharedPreferencesImpl>
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]   Summary:
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]       511 of java.lang.Class (7 unique instances)
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]         1 of android.app.SharedPreferencesImpl
    07-23 11:35:45.839  4252  4277 F art     : art/runtime/indirect_reference_table.cc:115]
    07-23 11:35:45.930  4252  4277 F art     : art/runtime/barrier.cc:90] Check failed: count_ == 0 (count_=-1, 0=0) Attempted to destroy barrier with non zero count
    07-23 11:35:45.930  4252  4277 F art     : art/runtime/runtime.cc:366] Runtime aborting --- recursively, so no thread-specific detail!
    07-23 11:35:45.930  4252  4277 F art     : art/runtime/runtime.cc:366]
    --------- beginning of crash
    07-23 11:35:45.930  4252  4277 F libc    : Fatal signal 6 (SIGABRT), code -6 in tid 4277 (.NET TP Worker)
    07-23 11:35:46.003  1640  1640 I SELinux : SELinux: Loaded file_contexts contexts from /file_contexts.
    07-23 11:35:46.006  1640  1640 F DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    07-23 11:35:46.006  1640  1640 F DEBUG   : Build fingerprint: 'Android/sdk_google_phone_x86_64/generic_x86_64:6.0/MASTER/6695544:userdebug/test-keys'
    07-23 11:35:46.006  1640  1640 F DEBUG   : Revision: '0'
    07-23 11:35:46.006  1640  1640 F DEBUG   : ABI: 'x86_64'
    07-23 11:35:46.006  1640  1640 F DEBUG   : pid: 4252, tid: 4277, name: .NET TP Worker  >>> com.microsoft.maui.essentials.devicetests <<<
    07-23 11:35:46.006  1640  1640 F DEBUG   : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
    07-23 11:35:46.014  1640  1640 F DEBUG   : Abort message: 'art/runtime/indirect_reference_table.cc:115] JNI ERROR (app bug): local reference table overflow (max=512)'
    07-23 11:35:46.014  1640  1640 F DEBUG   :     rax 0000000000000000  rbx 00007f7dadfbf500  rcx ffffffffffffffff  rdx 0000000000000006
    07-23 11:35:46.014  1640  1640 F DEBUG   :     rsi 00000000000010b5  rdi 000000000000109c
    07-23 11:35:46.014  1640  1640 F DEBUG   :     r8  0000000000000001  r9  0000000000000003  r10 0000000000000008  r11 0000000000000206
    07-23 11:35:46.014  1640  1640 F DEBUG   :     r12 00000000000010b5  r13 0000000000000006  r14 00007f7dc7305a40  r15 00007f7dabd70cc0
    07-23 11:35:46.014  1640  1640 F DEBUG   :     cs  0000000000000033  ss  000000000000002b
    07-23 11:35:46.014  1640  1640 F DEBUG   :     rip 00007f7dcaa49a67  rbp 0000000000000002  rsp 00007f7dadfbc048  eflags 0000000000000206
    07-23 11:35:46.018  1640  1640 F DEBUG   :
    07-23 11:35:46.018  1640  1640 F DEBUG   : backtrace:
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #00 pc 0000000000087a67  /system/lib64/libc.so (tgkill+7)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #1 pc 0000000000085b11  /system/lib64/libc.so (pthread_kill+65)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #2 pc 000000000002e841  /system/lib64/libc.so (raise+17)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #3 pc 00000000000288fd  /system/lib64/libc.so (abort+61)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #4 pc 00000000004ffd65  /system/lib64/libart.so (art::Runtime::Abort()+341)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #5 pc 0000000000178d71  /system/lib64/libart.so (art::LogMessage::~LogMessage()+2865)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #6 pc 0000000000172dad  /system/lib64/libart.so (art::Barrier::~Barrier()+813)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #7 pc 000000000053de1a  /system/lib64/libart.so (art::ThreadList::Dump(std::__1::basic_ostream<char, std::__1::char_traits<char> >&)+394)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #8 pc 00000000004ffea4  /system/lib64/libart.so (art::Runtime::Abort()+660)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #9 pc 0000000000178d71  /system/lib64/libart.so (art::LogMessage::~LogMessage()+2865)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #10 pc 00000000002f00fd  /system/lib64/libart.so (art::IndirectReferenceTable::Add(unsigned int, art::mirror::Object*)+1005)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #11 pc 00000000003ee19a  /system/lib64/libart.so (art::JNI::CallObjectMethodV(_JNIEnv*, _jobject*, _jmethodID*, __va_list_tag*)+586)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #12 pc 00000000001abdf8  /system/lib64/libart.so (art::CheckJNI::CallMethodV(char const*, _JNIEnv*, _jobject*, _jclass*, _jmethodID*, __va_list_tag*, art::Primitive::Type, art::InvokeType)+1736)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #13 pc 00000000001ae031  /system/lib64/libart.so (art::CheckJNI::CallObjectMethodV(_JNIEnv*, _jobject*, _jmethodID*, __va_list_tag*)+33)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #14 pc 000000000004555d  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonodroid.so (_JNIEnv::CallObjectMethod(_jobject*, _jmethodID*, ...)+157)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #15 pc 000000000003beb5  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonodroid.so (xamarin::android::internal::MonodroidRuntime::get_java_class_name_for_TypeManager(_jclass*)+69)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #16 pc 00000000001e8585  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #17 pc 00000000001e7020  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #18 pc 00000000001d8fb5  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #19 pc 00000000001d6961  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #20 pc 00000000000e5f19  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #21 pc 00000000002a96c6  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so (mono_runtime_invoke_checked+86)
    07-23 11:35:46.019  1640  1640 F DEBUG   :     #22 pc 00000000002b202e  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so
    07-23 11:35:46.020  1640  1640 F DEBUG   :     #23 pc 000000000026cb84  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so
    07-23 11:35:46.020  1640  1640 F DEBUG   :     #24 pc 000000000027615a  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so
    07-23 11:35:46.020  1640  1640 F DEBUG   :     #25 pc 00000000001e8618  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so
    07-23 11:35:46.020  1640  1640 F DEBUG   :     #26 pc 00000000001e7066  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so
    07-23 11:35:46.020  1640  1640 F DEBUG   :     #27 pc 00000000001d8fb5  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so
    07-23 11:35:46.020  1640  1640 F DEBUG   :     #28 pc 00000000001d6961  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so
    07-23 11:35:46.020  1640  1640 F DEBUG   :     #29 pc 00000000000e5f19  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so
    07-23 11:35:46.020  1640  1640 F DEBUG   :     #30 pc 00000000002a96c6  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so (mono_runtime_invoke_checked+86)
    07-23 11:35:46.020  1640  1640 F DEBUG   :     #31 pc 00000000002c0713  /data/app/com.microsoft.maui.essentials.devicetests-1/lib/x86_64/libmonosgen-2.0.so
    07-23 11:35:46.020  1640  1640 F DEBUG   :     #32 pc 0000000000084eee  /system/lib64/libc.so (__pthread_start(void*)+46)
    07-23 11:35:46.020  1640  1640 F DEBUG   :     #33 pc 00000000000296eb  /system/lib64/libc.so (__start_thread+11)
    07-23 11:35:46.020  1640  1640 F DEBUG   :     #34 pc 000000000001ce55  /system/lib64/libc.so (__bionic_clone+53)
    07-23 11:35:46.179  1640  1640 F DEBUG   :
    07-23 11:35:46.179  1640  1640 F DEBUG   : Tombstone written to: /data/tombstones/tombstone_00

After some investigation, we think this was introduced in 35f41dc.

I could reproduce the issue in a simple test case:

    [Test]
    public void PutAndGetManyValues ()
    {
        for (int i = 0; i < Count; i++) {
            using var prefs = GetPreferences ();
            using var editor = prefs.Edit ();
            editor.PutString ("key" + i, "value" + i);
            editor.Apply ();
        }

        for (int i = 0; i < Count; i++) {
            using var prefs = GetPreferences ();
            Assert.AreEqual ("value" + i, prefs.GetString ("key" + i, null));
        }
    }

Which also crashed with:

    07-24 09:39:02.615  4623  4638 I NUnit   : SharedPreferencesTest
    07-24 09:39:02.615  4623  4638 I NUnit   : PutAndGetManyValues
    07-24 09:39:02.719  4623  4638 F art     : art/runtime/indirect_reference_table.cc:115] JNI ERROR (app bug): local reference table overflow (max=512)

I updated `TypeManager` to use a `try-finally` block, such as:

    JniObjectReference typeClass = default;
    JniObjectReference handleClass = default;
    try {
        //...
    } finally {
        JniObjectReference.Dispose (ref handleClass);
        JniObjectReference.Dispose (ref typeClass);
    }

And it appears the test case now passes on API 23 emulators. Hoping
this will also fix .NET MAUI.
  • Loading branch information
jonathanpeppers authored Jul 25, 2024
1 parent c89de29 commit ab412a5
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 11 deletions.
27 changes: 16 additions & 11 deletions src/Mono.Android/Java.Interop/TypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -322,21 +322,26 @@ internal static IJavaPeerable CreateInstance (IntPtr handle, JniHandleOwnership
if (!typeSig.IsValid || typeSig.SimpleReference == null) {
throw new ArgumentException ($"Could not determine Java type corresponding to `{type.AssemblyQualifiedName}`.", nameof (targetType));
}
JniObjectReference typeClass;

JniObjectReference typeClass = default;
JniObjectReference handleClass = default;
try {
typeClass = JniEnvironment.Types.FindClass (typeSig.SimpleReference);
} catch (Exception e) {
throw new ArgumentException ($"Could not find Java class `{typeSig.SimpleReference}`.",
nameof (targetType),
e);
}
try {
typeClass = JniEnvironment.Types.FindClass (typeSig.SimpleReference);
} catch (Exception e) {
throw new ArgumentException ($"Could not find Java class `{typeSig.SimpleReference}`.",
nameof (targetType),
e);
}

var handleClass = JniEnvironment.Types.GetObjectClass (new JniObjectReference (handle));
if (!JniEnvironment.Types.IsAssignableFrom (handleClass, typeClass)) {
Logger.Log (LogLevel.Info, "*jonp*", $"# jonp: can't assign `{GetClassName(handleClass.Handle)}` to `{GetClassName(typeClass.Handle)}`");
handleClass = JniEnvironment.Types.GetObjectClass (new JniObjectReference (handle));
if (!JniEnvironment.Types.IsAssignableFrom (handleClass, typeClass)) {
Logger.Log (LogLevel.Info, "*jonp*", $"# jonp: can't assign `{GetClassName(handleClass.Handle)}` to `{GetClassName(typeClass.Handle)}`");
return null;
}
} finally {
JniObjectReference.Dispose (ref handleClass);
JniObjectReference.Dispose (ref typeClass);
return null;
}

IJavaPeerable? result = null;
Expand Down
34 changes: 34 additions & 0 deletions tests/Mono.Android-Tests/Android.Content/SharedPreferencesTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System;
using Android.App;
using Android.Content;
using NUnit.Framework;

namespace Android.ContentTests;

[TestFixture]
public class SharedPreferencesTest
{
const string Name = "testpreferences";
const int Count = 1000;

ISharedPreferences GetPreferences () =>
Application.Context.GetSharedPreferences (Name, FileCreationMode.Private);

// NOTE: test case on API 23 can trigger:
// art/runtime/indirect_reference_table.cc:115] JNI ERROR (app bug): local reference table overflow
[Test]
public void PutAndGetManyValues ()
{
for (int i = 0; i < Count; i++) {
using var prefs = GetPreferences ();
using var editor = prefs.Edit ();
editor.PutString ("key" + i, "value" + i);
editor.Apply ();
}

for (int i = 0; i < Count; i++) {
using var prefs = GetPreferences ();
Assert.AreEqual ("value" + i, prefs.GetString ("key" + i, null));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Android.App\ApplicationTest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Android.Content.Res\AssetManagerTest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Android.Content\IntentTest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Android.Content\SharedPreferencesTest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Android.Graphics\NinePatchTests.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Android.Runtime\AndroidEnvironmentTest.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Android.Runtime\CharSequenceTest.cs" />
Expand Down

0 comments on commit ab412a5

Please sign in to comment.