Forum Xamarin Xamarin.iOS

How to find out why some native code is constantly calling the IntPtr constructor?

BrainloopBrainloopBrainloopBrainloop DEBeta, University ✭✭

I have seen my app crashing and figured out that it wants to call something on a garbage collected object.
So I added the IntPtr c'tor to the class in question and put a Console.WriteLine("IntPtr!") in there.
Now I can see that something is calling that c'tor twice a second.
I tried to attach gdb to it but cannot see what's causing the call (see output below). How can I find out what method it is trying to call?

(gdb) thread apply all backtrace

Thread 12 (process 18402):
#0  0x938570ee in __workq_kernreturn ()
#1  0x96f470ac in _pthread_workq_return ()
#2  0x96f46e79 in _pthread_wqthread ()
#3  0x96f2ed2a in start_wqthread ()

Thread 8 (process 18402):
#0  0x938547d2 in mach_msg_trap ()
#1  0x93853cb0 in mach_msg ()
#2  0x04297a49 in __CFRunLoopServiceMachPort ()
#3  0x0429c8d4 in __CFRunLoopRun ()
#4  0x0429bf44 in CFRunLoopRunSpecific ()
#5  0x0429be1b in CFRunLoopRunInMode ()
#6  0x085a0c50 in RunWebThread ()
#7  0x96f445b7 in _pthread_start ()
#8  0x96f2ed4e in thread_start ()

Thread 7 (process 18402):
#0  0x93856c72 in __semwait_signal ()
#1  0x04b92770 in nanosleep ()
#2  0x04b92632 in usleep ()
#3  0x003f98a2 in monotouch_pump_gc (context=0x0) at monotouch-glue.m:597
#4  0x96f445b7 in _pthread_start ()
#5  0x96f2ed4e in thread_start ()

Thread 6 (process 18402):
#0  0x93856b3e in recvfrom$UNIX2003 ()
#1  0x04b928d8 in recv ()
#2  0x003f5581 in recv_uninterrupted (fd=5, buf=0xb039abbd, len=11) at debug.m:368
#3  0x003f5664 in sdb_recv (buf=0xb039abbd, len=11) at debug.m:384
#4  0x00285f6a in transport_recv [inlined] () at :1408
#5  0x00285f6a in debugger_thread (arg=0x0) at debugger-agent.c:7934
#6  0x003db4ea in thread_start_routine (args=0xc8b7f2c) at wthreads.c:287
#7  0x00386390 in gc_start_thread (arg=0xc61ee90) at sgen-gc.c:6280
#8  0x96f445b7 in _pthread_start ()
#9  0x96f2ed4e in thread_start ()

Thread 5 (process 18402):
#0  0x9385480e in semaphore_wait_trap ()
#1  0x003e3b5a in mono_sem_wait (sem=Cannot access memory at address 0x0
) at mono-semaphore.c:115
#2  0x002e8d3a in finalizer_thread (unused=0x0) at gc.c:1073
#3  0x003a95a6 in start_wrapper_internal [inlined] () at :784
#4  0x003a95a6 in start_wrapper (data=0xc61e110) at threads.c:832
#5  0x003db4ea in thread_start_routine (args=0xc8b7e34) at wthreads.c:287
#6  0x00386390 in gc_start_thread (arg=0xc61eb40) at sgen-gc.c:6280
#7  0x96f445b7 in _pthread_start ()
#8  0x96f2ed4e in thread_start ()

Thread 4 (process 18402):
#0  0x938547d2 in mach_msg_trap ()
#1  0x93853cb0 in mach_msg ()
#2  0x002b4a8a in mach_exception_thread (arg=0x0) at mini-darwin.c:132
#3  0x96f445b7 in _pthread_start ()
#4  0x96f2ed4e in thread_start ()

Thread 3 (process 18402):
#0  0x93856b3e in recvfrom$UNIX2003 ()
#1  0x04b928d8 in recv ()
#2  0x003f5581 in recv_uninterrupted (fd=7, buf=0xb01148df, len=1) at debug.m:368
#3  0x003f6deb in monotouch_process_connection (fd=7) at debug.m:769
#4  0x003f634f in monotouch_connect_wifi (ips=0xc61bfd0) at debug.m:562
#5  0x003f5267 in monotouch_configure_debugging () at debug.m:303
#6  0x003f4245 in -[CocoaThreadInitializer entryPoint:] (self=0xc3217f0, _cmd=0x55cfff, obj=0x0) at main.m:509
#7  0x00e00805 in -[NSThread main] ()
#8  0x00e00764 in __NSThread__main__ ()
#9  0x96f445b7 in _pthread_start ()
#10 0x96f2ed4e in thread_start ()

Thread 2 (process 18402):
#0  0x938579ca in kevent64 ()
#1  0x04afec2b in _dispatch_mgr_invoke ()
#2  0x04afe8cc in _dispatch_mgr_thread ()

Thread 1 (process 18402):
#0  0x938547d2 in mach_msg_trap ()
#1  0x93853cb0 in mach_msg ()
#2  0x04297a49 in __CFRunLoopServiceMachPort ()
#3  0x0429c84b in __CFRunLoopRun ()
#4  0x0429bf44 in CFRunLoopRunSpecific ()
#5  0x0429be1b in CFRunLoopRunInMode ()
#6  0x051a47e3 in GSEventRunModal ()
#7  0x051a4668 in GSEventRun ()
#8  0x01cbdffc in UIApplicationMain ()
#9  0x10bc5cd5 in ?? ()
#10 0x10bc3988 in ?? ()
#11 0x10bc3554 in ?? ()
#12 0x10bc36c6 in ?? ()
#13 0x001e3fc2 in mono_jit_runtime_invoke (method=0xd8f4fd4, obj=0x0, params=0xbffff338, exc=0x0) at mini.c:5804
#14 0x0034925e in mono_runtime_invoke (method=0xd8f4fd4, obj=0x0, params=0xbffff338, exc=0x0) at object.c:2790
#15 0x0034d6b4 in mono_runtime_exec_main (method=0xd8f4fd4, args=0xc76f7c8, exc=0x0) at object.c:3972
#16 0x0034da25 in mono_runtime_run_main (method=0xd8f4fd4, argc=0, argv=0x5f4e00, exc=0x0) at object.c:3602
#17 0x0023f9a5 in mono_jit_exec (domain=0xc61d510, assembly=0xc634760, argc=1, argv=0x5f4e00) at driver.c:1125
#18 0x003f4164 in main (argc=4, argv=0xbffff464) at main.m:482

Best Answer

Answers

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Adding the IntPtr constructor does not actually solve the problem. It fixes a crash, but it still probably isn't doing what you want. If your managed object is GCed and then recreated with the IntPtr constructor then you just lost all the state that your managed object had. There's no way to get that state back. If you really want to solve the problem then you should prevent it from being GCed by holding a reference to it somewhere.

    I suspect the reason this object is being continuously reconstructed is because something in the UI thread keeps using it, but nothing is there to keep in from leaving memory after each time it is used. Maybe there's a timer or something which ends up using it. Either way, the fix is still to prevent it from being GCed.

  • BrainloopBrainloopBrainloopBrainloop DEBeta, University ✭✭

    @adamkemp All what you say is correct. But it does not help me. I already know that something keeps on reconstructing the managed object and the state is lost. I want to figure out which method is called on the native side that leads to the reconstruction of the managed side. I could now add overrides for all methods in the managed class and place breakpoints and see which one gets hit. Then I know what unmanaged methods caused the problem. But that's just too much effort. I'm hoping for an easier solution.

  • adamkempadamkemp USInsider, Developer Group Leader mod

    I feel your pain. I have been there before. I thought that Xamarin added some info to the exception to show you which method was being called, though. Try looking in the debug output.

  • SebastienPouliotSebastienPouliot CAXamarin Team Xamurai

    @ReneRuppert It's not clear how/when you stopped gdb ? IOW are the backtraces supposed to show us when the .ctor was being called ? or when the app crashed ?

    When I use CWL (before I get to gdb) I often print an unique id (if I add more than one) and the current stack trace, e.g.:

    Console.WriteLine ("XXX {0}", Environment.StackTrace);
    

    That generally gives me an better idea of when that code is called and where I should put my breakpoints (in XS or gdb). OTOH for this case I suspect you'll only see the runtime trying to re-construct the previous instance (and giving up to create a new instance).

  • BrainloopBrainloopBrainloopBrainloop DEBeta, University ✭✭

    @poupou I don't know what "CWL" is, but here's the stack trace if the IntPtr c'tor is hit. You can get the demo project at https://github.com/Krumelur/XamarinBindings and run the example "PSPDFViewController with NULL document" three times and the PSPDFKitDemoXamarin.iOS.KSNoDocumentPDFViewController() constructor will start being called constantly.

    2013-05-22 09:16:19.941 PSPDFKitDemoXamariniOS[46674:c07] KSNoDocumentPDFViewController    at System.Environment.get_StackTrace() in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System/Environment.cs:line 252
       at PSPDFKitDemoXamarin.iOS.KSNoDocumentPDFViewController..ctor(IntPtr handle) in /Users/rene/Documents/Develop/PSPDFKit/KS_PSPDFKit_Bindings/KS_PSPDFKit-Demo-Xamarin.iOS/Subclassing/KSNoDocumentPDFViewController.cs:line 18
       at System.Reflection.MonoCMethod.InternalInvoke(System.Reflection.MonoCMethod , System.Object , System.Object[] , System.Exception ByRef )
       at System.Reflection.MonoCMethod.Invoke(System.Object obj, BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Reflection/MonoMethod.cs:line 530
       at System.Reflection.MonoCMethod.Invoke(BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Reflection/MonoMethod.cs:line 545
       at System.Reflection.ConstructorInfo.Invoke(System.Object[] parameters) in /Developer/MonoTouch/Source/mono/mcs/class/corlib/System.Reflection/ConstructorInfo.cs:line 62
       at MonoTouch.ObjCRuntime.Runtime.ConstructNSObject(IntPtr ptr, IntPtr klass) in /Developer/MonoTouch/Source/monotouch/src/ObjCRuntime/Runtime.cs:line 269
       at MonoTouch.ObjCRuntime.Runtime.GetNSObject(IntPtr ptr) in /Developer/MonoTouch/Source/monotouch/src/ObjCRuntime/Runtime.cs:line 328
       at MonoTouch.ObjCRuntime.Runtime.GetNSObjectWrapped(IntPtr ptr) in /Developer/MonoTouch/Source/monotouch/src/ObjCRuntime/Runtime.cs:line 345
       at MonoTouch.Foundation.NSObject.monotouch_release_managed_ref(IntPtr )
       at MonoTouch.Foundation.NSObject.ReleaseManagedRef() in /Developer/MonoTouch/Source/monotouch/src/Foundation/NSObject.cs:line 98
       at MonoTouch.Foundation.NSObject+NSObject_Disposer.Drain(MonoTouch.Foundation.NSObject ctx) in /Developer/MonoTouch/Source/monotouch/src/shared/Foundation/NSObject2.cs:line 545
       at MonoTouch.UIKit.UIApplication.UIApplicationMain(Int32 , System.String[] , IntPtr , IntPtr )
       at MonoTouch.UIKit.UIApplication.Main(System.String[] args, System.String principalClassName, System.String delegateClassName) in /Developer/MonoTouch/Source/monotouch/src/UIKit/UIApplication.cs:line 38
       at PSPDFKitDemoXamarin.iOS.Application.Main(System.String[] args) in /Users/rene/Documents/Develop/PSPDFKit/KS_PSPDFKit_Bindings/KS_PSPDFKit-Demo-Xamarin.iOS/Main.cs:line 17
    
  • BrainloopBrainloopBrainloopBrainloop DEBeta, University ✭✭
    edited May 2013

    @poupou Isn't the GC called every second in the Simulator? I think I have some kind of of circle here that releases managed object, that somehow creates unmanaged object and the game restarts.

    I found the culprit. If I remove the binding

    [Export ("rotationLockEnabled", ArgumentSemantic.Assign)]
    bool RotationLockEnabled { [Bind ("isRotationLockEnabled")] get; set; }
    

    it works.

    If I look at the ObjC source, I can see that dealloc() is calling exactly that property. How can I fix this? I need the binding. Maybe this one is also interesting for @dalexsoto.

    - (void)dealloc {
    [...]
        // Release rotation lock if still locked.
        if (self.isRotationLockEnabled) PSPDFUnlockRotation();
    
        // Clear memory after the last PSPDFViewController has been deallocated.
        if (OSAtomicDecrement32Barrier(&_numberOfInstances) == 0) {
            pspdf_dispatch_async_if(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), !PSPDFIsCrappyDevice(), ^{
                [PSPDFCache.sharedCache.memoryCache clearCache];
            });
        }
    }
    

    This looks like to be related to that bug: https://bugzilla.xamarin.com/show_bug.cgi?id=6338

    I presume we are facing a similar issue in the bug we submitted: https://bugzilla.xamarin.com/show_bug.cgi?id=12281 (although this particular change here does not fix the problem mentioned in the bug report)

    Looks like we cannot get rid of some instances, no matter what we do.

  • SebastienPouliotSebastienPouliot CAXamarin Team Xamurai

    The ObjC dealloc is automatically called when nothing, managed or native, has a reference to that instance (refcount == 0). That means the .NET instance has already been disposed (while the native instance is about the be).

    What 'dealloc' does here is to call a virtual method inside it's finalizer, which can be problematic. In this case it force the instance to be resurfaced in the managed world (so the binding for isRotationLockEnabled can be called).

    The stack trace, from your previous comment, shows this happening (i.e. Drain -> ReleaseManagedRef -> native -> GetNSObjectWrapped).

    p.s. CWL is a TLA for Console.WriteLine

  • BrainloopBrainloopBrainloopBrainloop DEBeta, University ✭✭

    @poupou Are you saying there is no workaround/fix for this and it is a limitation of managed and unmanaged code living side by side? I will have to remove the binding and that's all I can do?

    Is this "problematic" in .NET or also in ObjC? (I mean, calling a virtual method - in ObjC everything is virtual anyway...)

  • SebastienPouliotSebastienPouliot CAXamarin Team Xamurai

    @ReneRuppert It's potentially problematic in a language agnostic way, i.e. you never know how you'll be overridden. It would be easy to write code that breaks this in ObjC.

    There are likely several workarounds, but all the ones I have in mind have some brawbacks. e.g.

    • removing the property binding (so it's not overriden), like you did (but you lose the feature);

    • fixing the original code, like implementing the rotationLockEnabled getter/setter explicitly (I assume it's implicit in the source), setting a variable that will be used at dealloc time (so no virtual call will be done). OTOH you might have the source but may not fancy maintaining your fork (unless it can be merged back upstream);

    It's also not 100% clear why this crash your application (calling retain on dealloc is bad but it might not be the cause).

  • BrainloopBrainloopBrainloopBrainloop DEBeta, University ✭✭

    @Adamkemp and @poupou Exactly, the crash was before I added the IntPtr c'tor. After adding add, I ran into this circle of dealloc-reincarnate.

    Bonus question: PSPDFKit is compared to the complete Apple API rather small. I would assume that Apple's APIs contains thousands of potential traps like the one I encountered. Or is PSPDFKit coded in a very special style that makes it prone to these kinds of issues?

  • SebastienPouliotSebastienPouliot CAXamarin Team Xamurai

    They likely (I hope) have some static analysis tools [1] to help them find such occurances or good code reviews for common issues [2]. Adam's quote from Apple documentation makes it clear they know about the dangers.

    [1] E.g. both FxCop and Gendarme have a rule to check this inside constructors

    [2] like dealloc should only dealloc ;-)

  • adamkempadamkemp USInsider, Developer Group Leader mod

    Back when we were coding our iOS apps in ObjectiveC we had watch for that kind of error as well, and I remember in some code reviews someone caught it and we fixed it. It's just something you're not supposed to do. The writers of PSPDFKit just did it wrong, and it should be fixed in their code.

  • BrainloopBrainloopBrainloopBrainloop DEBeta, University ✭✭

    @AdamKemp It is fixed already in PSPDFKit.

Sign In or Register to comment.