Bug 53620 - GC collecting 'pinned' arrays on call to bindings in Xamarin.iOS
Summary: GC collecting 'pinned' arrays on call to bindings in Xamarin.iOS
Status: RESOLVED INVALID
Alias: None
Product: iOS
Classification: Xamarin
Component: Xamarin.iOS.dll ()
Version: XI 10.4 (C9)
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: Untriaged
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2017-03-21 10:10 UTC by Antonio
Modified: 2017-03-30 09:36 UTC (History)
3 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED INVALID

Description Antonio 2017-03-21 10:10:48 UTC
Good Morning,

I've had the same Xamarin.iOS bindings to native code for roughly 3 years working flawlessly but lately i've noticed a crash behaviour that might have been there since the beginning. I deal with bindings of large (and also small) arrays like images and so on.

The issue: It seems like the GC is collecting some of the structures while the unmanaged calls to native are operating. I am 'pinning' the data involved and method binding calls with unsafe fixed statements.

fixed (double* px = _px)
{
    fixed (double* py = _py)
    {
        fixed (double* param = parameters)
        {
            fixed (int* distMap = _distMap)
            {
                IntPtr dataPxPtr = new IntPtr((void*)px);
                IntPtr dataPyPtr = new IntPtr((void*)py);
                IntPtr parametersPtr = new IntPtr((void*)param);
                IntPtr distMapPtr = new IntPtr((void*)distMap);
                _bindings.Initiate(dataPxPtr, dataPyPtr, parametersPtr, distMapPtr, 1E-4, Size.Width);
                _bindings.Iterate();
            }
        }
    }
}

Theoretically the nested fixed statement should pin the memory addresses whilst the binding methods run. But that doesn't seem to be the case...


Xamarin Side Stacktrace

Warning (239) / iOSApp: critical:   at <unknown> <0xffffffff>
Warning (239) / iOSApp: critical:   at (wrapper managed-to-native) ...Messaging.void_objc_msgSend (intptr,intptr) <0x00007>
Warning (239) / iOSApp: critical:   at ...Bindings.Iterate () <0x00047>

XCode Stacktrace

Exception Type:  EXC_BAD_ACCESS (SIGABRT)
Exception Subtype: KERN_INVALID_ADDRESS at 0x00000001088fc018
Triggered by Thread:  4

Thread 4 name:  tid_681f
Thread 4 Crashed:
0   libsystem_kernel.dylib          0x0000000182cac11c __pthread_kill + 8
1   libsystem_pthread.dylib         0x0000000182d78ef8 pthread_kill + 112
2   libsystem_c.dylib               0x0000000182c1ddc8 abort + 140
3   iOSApp                          0x0000000100ed26cc mono_handle_native_sigsegv (mini-exceptions.c:2462)
4   iOSApp                          0x0000000100eddccc mono_sigsegv_signal_handler (mini-runtime.c:2906)
5   libsystem_platform.dylib        0x0000000182d7194c _sigtramp + 68
6   iOSApp                          0x00000001000ed77c lmdif_ (lmdif_.c:468)
7   iOSApp                          0x00000001000f1e44 lmdif1_ (lmdif1_.c:153)
8   iOSApp                          0x00000001000ec3c8 -[Bindings iterate] (Bindings.m:403)
9   iOSApp                          0x0000000101bf1698 wrapper_managed_to_native_Bindings_Messaging_void_objc_msgSend_intptr_intptr (/<unknown>:1)
10  iOSApp                          0x0000000101bf0428 Bindings_Iterate (/<unknown>:1)



I've tried, with no success:

Attempting to make deep copies in C# to IntPtr and pass them to bindings
Attempting to make deep copies in obj-c as soon as they are passed to the bindings
Attempting pinning of arrays in C# managed side (GCHandle) and use them to pass to the bings (gchandle.free() after obv)
others i cant remember...
Any help would be appreciated as i'm going nuts.

Thanks,

Antonio
Comment 1 Manuel de la Peña [MSFT] 2017-03-21 16:48:15 UTC
Hello,

Firstly can you provide the exact version in which this issues has occurred and in which it used not to happen? It might be a regression as far as we know. Also, if you provide a small sample (you can do in a private comment) would be of great help since we could identify the possible changes that we made from one version to another that made this bug appear or be more common.

Regards,

Manuel
Comment 2 Antonio 2017-03-22 10:00:15 UTC
Morning Manuel,

Thanks for your answer.

1 - Sorry for missing the Xamarin.iOS version but i think i've selected it in the bug fields? Anyway my environment follows:

=== Xamarin Studio Enterprise ===

Version 6.2 (build 1821)
Installation UUID: 19ac9404-6aab-4588-ac4c-3b36eb439e42
Runtime:
	Mono 4.8.0 (mono-4.8.0-branch/e4a3cf3) (64-bit)
	GTK+ 2.24.23 (Raleigh theme)

	Package version: 408000495

=== NuGet ===

Version: 3.5.0.0

=== Xamarin.Profiler ===

'/Applications/Xamarin Profiler.app' not found

=== Xamarin.Android ===

Version: 7.1.0.41 (Visual Studio Enterprise)
Android SDK: /Users/antoniolopes/Library/Developer/Xamarin/android-sdk-macosx
	Supported Android versions:
		4.2 (API level 17)
		4.4 (API level 19)
		5.0 (API level 21)
		5.1 (API level 22)
		6.0 (API level 23)
		7.0 (API level 24)
		7.1 (API level 25)

SDK Tools Version: 25.2.5
SDK Platform Tools Version: 25.0.3
SDK Build Tools Version: 24.0.3

Java SDK: /usr
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)

Android Designer EPL code available here:
https://github.com/xamarin/AndroidDesigner.EPL

=== Xamarin Android Player ===

Not Installed

=== Apple Developer Tools ===

Xcode 8.2.1 (11766.1)
Build 8C1002

=== Xamarin.Mac ===

Not Installed

=== Xamarin.iOS ===

Version: 10.4.0.123 (Visual Studio Enterprise)
Hash: 35d1ccd
Branch: cycle9
Build date: 2017-02-16 17:40:00-0500

=== Xamarin Inspector ===

Not Installed

=== Build Information ===

Release ID: 602001821
Git revision: d41b6e51f3fa46a1943f2e31a778d28a7c73d069
Build date: 2017-02-17 15:18:19-05
Xamarin addins: 1363a8d943bab7700c93a97474060b6734aa7f94
Build lane: monodevelop-lion-cycle9

=== Operating System ===

Mac OS X 10.12.1
Darwin Antonios-Mac-mini.local 16.1.0 Darwin Kernel Version 16.1.0
    Wed Oct 19 20:31:56 PDT 2016
    root:xnu-3789.21.4~4/RELEASE_X86_64 x86_64

=== Enabled user installed addins ===

StyleCop Support 1.0.1.9

2 - I can't provide you with the project where this happens. Its an IP protected library and even with NDA and private attachment in place i don't think its worth sharing - afterall its a self contained problem.
What i can do is to build a test case specifically for this (empty XS project linking to a ios .a lib) but the issue might not happen in that test case. Also bear in mind that will take me some time to put together so needs discussion in our company.

3 - Im struggling to understand that Xamarin doesn't have the tools to replicate this self contained case. I do understand that its easier to nail down if i provide a test case but surely you have a test harness in CI regarding bindings to test this kind of communication to native code?

4 - This is a GC issue. Can you clarify if the heuristics of GC or its implementation is owned by Xamarin? Can regression happen in Xamarin that affects GC??

Let me know your thoughts.

Thanks
Comment 3 Rolf Bjarne Kvinge [MSFT] 2017-03-22 10:25:44 UTC
(In reply to Antonio from comment #2)
> Morning Manuel,
> 2 - I can't provide you with the project where this happens. Its an IP
> protected library and even with NDA and private attachment in place i don't
> think its worth sharing - afterall its a self contained problem.
> What i can do is to build a test case specifically for this (empty XS
> project linking to a ios .a lib) but the issue might not happen in that test
> case. Also bear in mind that will take me some time to put together so needs
> discussion in our company.
> 
> 3 - Im struggling to understand that Xamarin doesn't have the tools to
> replicate this self contained case. I do understand that its easier to nail
> down if i provide a test case but surely you have a test harness in CI
> regarding bindings to test this kind of communication to native code?

We use a lot of fixed buffers ourselves, and we also have a lot of tests. None show any problems like this one.

This suggests that there's something specific to your code (or the native library) that triggers this problem, and if that's the case, we won't be able to reproduce it without a test case.

> 4 - This is a GC issue. Can you clarify if the heuristics of GC or its
> implementation is owned by Xamarin? Can regression happen in Xamarin that
> affects GC??

Regressions can always happen, and memory issues like this one can be tricky (it might not even be a regression, it could be that iOS itself gives out memory differently, causing a bug that was always there to suddenly cause problems now).

One thing you can try is to eliminate the GC from the equation, by allocating unmanaged memory and passing that to the native functions:

	IntPtr dataPxPtr = Marshal.AllocHGlobal (sizeof (double) * _px.Length);
	Marshal.Copy (_px, 0, dataPxPtr, _px.Length);
	IntPtr dataPyPtr = Marshal.AllocHGlobal (sizeof (double) * _py.Length);
	Marshal.Copy (_py, 0, dataPyPtr, _py.Length);
	IntPtr parametersPtr = Marshal.AllocHGlobal (sizeof (double) * parameters.Length);
	Marshal.Copy (parameters, 0, parametersPtr, parameters.Length);
	IntPtr distMapPtr = Marshal.AllocHGlobal (sizeof (int) * _distmap.Length);
	Marshal.Copy (_distmap, 0, distMapPtr, _distmap.Length);
	_bindings.Initiate(dataPxPtr, dataPyPtr, parametersPtr, distMapPtr, 1E-4, Size.Width);
	_bindings.Iterate();

If this still crashes, it's not a problem with the GC, but either in the input (arrays are smaller than what the native function expects), or in the native functions.
Comment 4 Antonio 2017-03-22 10:35:44 UTC
Thanks Rolf.

Your comments make total sense.

I have a lot of hope on the fact that it is on the native library side, i.e. the crash always happens on the same call/lines within the native library. I think this is a good sign that might not be GC and might be array binding issues.

I've tried your suggestion before (amongst others):
"Attempting to make deep copies in C# to IntPtr and pass them to bindings
Attempting to make deep copies in obj-c as soon as they are passed to the bindings
Attempting pinning of arrays in C# managed side (GCHandle) and use them to pass to the bings (gchandle.free() after obv)"

I think allocating unmanaged memory was my try that reduced the frequency of the issue. I'll retry today and observe.

After that i'll make and implement a test case using the "problematic" code on native side.

Don't know how relevant this is for now (or for you Rolf) but the native library code where the issue always occurs is a fairly established non linear minimization library called cminpack. So basically my native library is a big obj-c wrapper for a lot of C code.
Comment 5 Antonio 2017-03-30 09:35:03 UTC
@Rolf

Morning Rolf,

Only had the chance now to feedback.

I meanwhile confirmed tha the bug is not xamarin or GC related but rather bad behaviour of native code used. Indeed indexing exception.
Comment 6 Rolf Bjarne Kvinge [MSFT] 2017-03-30 09:36:05 UTC
@Antonio, thanks for getting back to us!