Bug 60986

Summary: Memory leak when marshalling Delegate to native code
Product: [Mono] Runtime Reporter: d.obrazcov
Component: InteropAssignee: Vlad Brezae <vlad.brezae>
Status: RESOLVED FIXED    
Severity: normal CC: anton.petrov.83, ludovic, masafa, mono-bugs+mono, mono-bugs+runtime, vlad.brezae
Priority: ---    
Version: 5.8 (2017-10)   
Target Milestone: ---   
Hardware: PC   
OS: Linux   
Tags: bugpool-archive Is this bug a regression?: ---
Last known good build:
Attachments: Test project that reproduce memory leak
Profile log

Description d.obrazcov 2017-12-05 13:15:19 UTC
In our project we send callback delegate to native code and found a memory leak at runtime.

This is a sample project that reproduce the problem.

class CallbackOwner
{
   [UnmanagedFunctionPointer( CallingConvention.Cdecl )]
   internal delegate void NativeFunctionCallback(int arg);

   [DllImport("libnative", CallingConvention = CallingConvention.Cdecl)]
   internal static extern void native_function(int arg, NativeFunctionCallback cb);

   private NativeFunctionCallback _cb;

   public CallbackOwner()
   {
      _cb = CallbackImplementation;
   }

   public void Run(int n)
   {
      native_function( n, _cb ); // Expecting leak here!
   }

   private void CallbackImplementation( int arg )
   {
      Console.WriteLine( arg );
   }
}

And write some loop to test

int n = 0;
while ( true )
{
   new CallbackOwner().Run( ++n );

   // Collect managed memory.
   if (n%1000 == 0)
      GC.Collect();
}

"libnative" is a simple test programm that only execute callback.

Full test project added to attachement.

This leak reproduced on Mac and Linux on Windows all fine. With mono-boehm leak not reproduced, only with SGen.

Profiler show memory leak at:

Leak: 0x7f81e74001c0  size=16  zone: DefaultMallocZone_0x1068f1000
	0x03b0600e 0x00000000 0x00000000 0x00000000 	.`..............
	Call stack: [thread 0x7fffb321e340]: | start | main | mono_main | mono_jit_exec | do_exec_main_checked | do_runtime_invoke | mono_jit_runtime_invoke | 0x106a0d427 | mono_delegate_to_ftnptr | mono_jit_compile_method_with_opt | mono_jit_compile_method_inner | mini_method_compile | mono_save_seq_point_info | mono_seq_point_info_new | monoeg_malloc0 | calloc | malloc_zone_calloc
Comment 1 d.obrazcov 2017-12-05 13:16:41 UTC
Created attachment 25912 [details]
Test project that reproduce memory leak
Comment 2 d.obrazcov 2017-12-05 13:18:25 UTC
Created attachment 25913 [details]
Profile log
Comment 3 a.petrov 2017-12-05 13:28:12 UTC
"$ leaks mono-sgen" revealed a lot of leaks like this one:

----------
Leak: 0x7f81e74001c0  size=16  zone: DefaultMallocZone_0x1068f1000
    0x03b0600e 0x00000000 0x00000000 0x00000000     .`..............
    Call stack: [thread 0x7fffb321e340]: | start | main | mono_main | mono_jit_exec | do_exec_main_checked | do_runtime_invoke | mono_jit_runtime_invoke | 0x106a0d427 | mono_delegate_to_ftnptr | mono_jit_compile_method_with_opt | mono_jit_compile_method_inner | mini_method_compile | mono_save_seq_point_info | mono_seq_point_info_new | monoeg_malloc0 | calloc | malloc_zone_calloc 
----------

We suspect that `mono_destroy_compile()` is not freeing MonoCompile's `seq_point_info` field. Looks like its missing `mono_seq_point_info_free()` call.
Comment 4 a.petrov 2017-12-05 23:03:53 UTC
Looks like previously reported leaks of 5.4 were fixed by https://github.com/mono/mono/commit/4cf2b5feee0c3aed6b747105c141f48165bd0ae1#diff-889eecc0e02f623fc21d69e97f0983c4

But memory of the sample application attached here still is growing rapidly. Looks like  stub functions generated for delegates marshaling are never deleted from memory (mono/metadata/marshal.c: mono_delegate_to_ftnptr()).
Comment 5 Ludovic Henry 2018-01-16 20:07:38 UTC
https://github.com/mono/mono/pull/6505