« February 2006 | Main | May 2006 »

April 30, 2006

NSZombie update

I wonder if I'm using cocoa bindings incorrectly... or maybe there's a bug. Usually when I start to think "maybe there's a bug in somebody else's code," I look for ways in which I'm using that code in weird (wrong) ways. I figure that the pyobjc people are about 7 orders of magnitude better at this than I am.

At least I'm getting closer to isolating the problem. Thanks to rewriting the gui and bindings from scratch last night while building and running a simplified version of the project backend after after each new binding (and thanks to finally having a few hours to work on my little hobbies),here's what I know so far. I'll be making a "proof of bug" concept here sometime today. I'll link to it here and post it on the pyobjc-dev list because the below is utterly confusing to me and I wrote it:

1. A regular pyobjc (not document-based) app with the usual appDelegate stuff involved.
2. A simple (doesn't know about objc) python class we'll call Simple (and pretend it has but one attribute we want, a list called 'ourFriends.'
3. A python class we'll call JSNSSimple that has an instance of Simple as an attribute. JSNSSimple subclasses NSObject. This class is defined in our NIB file as *only* a subclass of NSObject. We're using this class to wrap the functionality of Simple in getters and setters to be KVC complient ala cocoa. We've written friends_(self, friends) and getFriends(self). The constructor of JSNSSimple uses the NibClassBuilder.AutoBaseClass voodoo that allows the python object to get it's superclass from the NIB file.
4. We have an array controller to hold instances of JSNSSimple we'll call simpleArrayController that is also an attribute of our appDelegate.
5. We add and subtract instances of JSNSSimple through the appDelegate like so:
add:
newSimple = JSNSSimple.alloc().init()
self.simpleArrayController.addObject_(newSimple)

subtract:
if len(self.simpleArrayController.arrangedObjects()) > 0:
index = self.simpleArrayController.selectionIndex()
self.simpleArrayController.removeObjectAtArrangedObjectIndex_(index)
6. We add an NSTableView to our gui that is bound to some other KVC complient attribute of each JSNSSimple instance (the contents of the array controller) just to see that we really add and subtract JSNSSimple instances from the array. We find that this works.
7. We add an NSPopubButton to our GUI which we want to fill with our "ourFriends" list so that we can select a friend for one reason or another. We add a cocoa binding to follow each currently selected JSNSSimple instance friend attribute. This breaks. Adding one instance of JSNSSimple to the array works just peachy, but upon adding a second...
8. Somehow, in the pyobjc bridge (or perhaps in the way I'm using it incorrectly...) the pyobjc proxy object, an OC_PythonArray, that corresponds to our 'ourFriends' attribute in in the Simple (I think not JSNSSimple) class gets freed and somehow the binding really wants it to stick around. This causes a retain message to get sent to our OC_PythonArray when it's already been freed... and boom.
9. We pull out the binding that caused this crash and everything is back to OK...

Things I'll try next:
1. Remove the autoBaseClass nib file stuff and do a straight subclass of NSObject
2. Add attributes to the NSObject in interface builder that correspond to the 'ourFriends/friends' gettrs and setters.
3. Take a closer look at the stack trace and perhaps (gdb is our friend) break on the free/deallocation of the OC_PythonArray.
4. Give up and do it "a probably smarter way." The 'ourFriends' attribute of the Simple class can probably be accessed in another way. Perhaps we'll use an array controller for it, too.........

April 10, 2006

NSZombie eating my brain!

I've been learning cocoa bindings (I like this voodoo) and have hit a brick wall. I've been asking pythonmac-sig if they've any ideas and they pointed me in the direction of NSZombieEnabled, but i haven't been able to get very far.

I have an NSArray controller with a python object subclassing NSObject. I've put a + button on the gui to add the python object like this:::

def addTriggerModel_(self, sender): # BUTTON
self.setCanAddTriggerModel_(False)
newTriggerModel = scNSDrumTriggerModel.alloc().init()
newTriggerModel.resetWithNodeBusAndInput(node = self.nextNode, bus = self.nextBus, \
input = self.nextInput, sendInfo = self.sendingTriggersToClient)
self.nextNode += 100
self.nextBus += 10
self.nextInput += 1
NSLog("Adding newTriggerModel")
self.triggerArrayController.addObject_(newTriggerModel)
NSLog("Added newTriggerModel")
self.setCanAddTriggerModel_(True)


This works just peachy the first time I do it, but fails on a second addition of the new scNSDrumTriggerModel object. At first I was getting Signal 5/6 errors, then I did:

from PyObjCTools import Signals
Signals.dumpStackOnFatalSignal()
from PyObjCTools import Debugging
Debugging.installVerboseExceptionHandler()

This told me:

NSGenericException - *** Selector 'retain' sent to dealloced instance 0x1147cb0 of class OC_PythonArray

So I turned on NSZombieEnabled and tried running in gdb with a breakpoint on -[_NSZombie retain], but I don't really know what I'm looking for. I'm looking at a lot of hex memory addresses with very little human-readable stuff in there.

I tried setting a breakpoint at (+[OC_PythonArray newWithPythonObject:]) to see perhaps what proxy objects are being instantiated, but I'm clearly in over my depth. The variable corresponding to the pythonObject in OC_PythonArray newWithPythonObject: up there ^ has address 0x0 (Is that a null pointer?) in gdb each time I hit that breakpoint.

When I run malloc_history on the memory address that the NSZombie has, I get this:


malloc_history 506 0x1147cb0

Call [2] [arg=12]: thread_a000ed98 |0x0 | _dyld_start | 0x2414 | 0x3e50 | 0x3db0 | PyRun_SimpleFileExFlags | PyRun_FileExFlags | PyEval_EvalCode | PyEval_EvalCodeEx | PyEval_EvalCode | PyEval_GetFuncDesc | PyEval_GetFuncDesc | PyEval_EvalCodeEx | PyEval_EvalCode | PyEval_GetFuncDesc | _PyUnicodeUCS2_IsAlpha | PyRun_FileExFlags | PyEval_EvalCode | PyEval_EvalCodeEx | PyEval_EvalCode | PyEval_GetFuncDesc | PyEval_GetFuncDesc | PyEval_EvalCodeEx | PyEval_EvalCode | PyEval_GetFuncDesc | objc_NSApplicationMain | NSApplicationMain | +[NSBundle(NSNibLoading) loadNibNamed:owner:] | +[NSBundle(NSNibLoading) loadNibFile:externalNameTable:withZone:] | +[NSBundle(NSNibLoading) _loadNibFile:nameTable:withZone:ownerBundle:] | loadNib | -[NSKeyedUnarchiver initForReadingWithData:] | _CFPropertyListCreateFromXMLData | parsePListTag | parseDictTag | parseArrayTag | parseDictTag | parseDictTag | _CFKeyedArchiverUIDCreate | _CFRuntimeCreateInstance | CFAllocatorAllocate
Call [4] [arg=0]: thread_a000ed98 |0x0 | _dyld_start | 0x2414 | 0x3e50 | 0x3db0 | PyRun_SimpleFileExFlags | PyRun_FileExFlags | PyEval_EvalCode | PyEval_EvalCodeEx | PyEval_EvalCode | PyEval_GetFuncDesc | PyEval_GetFuncDesc | PyEval_EvalCodeEx | PyEval_EvalCode | PyEval_GetFuncDesc | _PyUnicodeUCS2_IsAlpha | PyRun_FileExFlags | PyEval_EvalCode | PyEval_EvalCodeEx | PyEval_EvalCode | PyEval_GetFuncDesc | PyEval_GetFuncDesc | PyEval_EvalCodeEx | PyEval_EvalCode | PyEval_GetFuncDesc | objc_NSApplicationMain | NSApplicationMain | +[NSBundle(NSNibLoading) loadNibNamed:owner:] | +[NSBundle(NSNibLoading) loadNibFile:externalNameTable:withZone:] | +[NSBundle(NSNibLoading) _loadNibFile:nameTable:withZone:ownerBundle:] | loadNib | -[NSKeyedUnarchiver dealloc] | _CFRelease | __CFArrayDeallocate | __CFArrayReleaseValues | _CFRelease | __CFDictionaryDeallocate | _CFRelease
Call [2] [arg=12]: thread_a000ed98 |0x0 | _dyld_start | 0x2414 | 0x3e50 | 0x3db0 | PyRun_SimpleFileExFlags | PyRun_FileExFlags | PyEval_EvalCode | PyEval_EvalCodeEx | PyEval_EvalCode | PyEval_GetFuncDesc | PyEval_GetFuncDesc | PyEval_EvalCodeEx | PyEval_EvalCode | PyEval_GetFuncDesc | _PyUnicodeUCS2_IsAlpha | PyRun_FileExFlags | PyEval_EvalCode | PyEval_EvalCodeEx | PyEval_EvalCode | PyEval_GetFuncDesc | PyEval_GetFuncDesc | PyEval_EvalCodeEx | PyEval_EvalCode | PyEval_GetFuncDesc | objc_NSApplicationMain | NSApplicationMain | -[NSApplication run] | -[NSApplication sendEvent:] | -[NSWindow sendEvent:] | -[NSControl mouseDown:] | -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] | -[NSCell trackMouse:inRect:ofView:untilMouseUp:] | -[NSCell _sendActionFrom:] | -[NSControl sendAction:to:] | -[NSApplication sendAction:to:from:] | ffi_closure_ASM | ffi_closure_helper_DARWIN | method_stub | PyObject_Call | pysel_call | PyObject_Call | PyFunction_SetClosure | PyEval_EvalCodeEx | PyEval_EvalCode | PyEval_GetFuncDesc | PyEval_GetFuncDesc | PyObject_Call | objcsel_call | PyObjCFFI_Caller | ffi_call | ffi_call_DARWIN | -[NSArrayController _insertObject:atArrangedObjectIndex:objectHandler:] | -[NSArrayController didChangeValuesForArrangedKeys:objectKeys:indexKeys:] | -[NSController _notifyObserversForKeyPath:change:] | -[NSObject(NSKeyValueObservingPrivate) _notifyObserversForKeyPath:change:] | -[NSSelectionBinder observeValueForKeyPath:ofObject:change:context:] | -[NSValueBinder _observeValueForKeyPath:ofObject:context:] | -[NSSelectionBinder _adjustObject:mode:observedController:observedKeyPath:context:editableState:adjustState:] | -[NSSelectionBinder _valueForBindingWithoutResolve:mode:] | -[NSBinder valueForBinding:resolveMarkersToPlaceholders:] | -[NSBinder _valueForKeyPath:ofObject:mode:raisesForNotApplicableKeys:] | -[NSObject(NSKeyValueCoding) valueForKeyPath:] | -[NSArrayController _singleValueForKeyPath:] | -[NSObject(NSKeyValueCoding) valueForKeyPath:] | ffi_closure_ASM | ffi_closure_helper_DARWIN | object_method_valueForKey_ | _NSGetUsingKeyValueGetter | ffi_closure_ASM | ffi_closure_helper_DARWIN | method_stub | depythonify_c_return_value | depythonify_c_value | +[OC_PythonObject wrapPyObject:toId:] | +[OC_PythonArray newWithPythonObject:] | NSAllocateObject | _internal_class_createInstanceFromZone

Here is the bt when gdb hits NSZombie retain.
#0 0x92a43c80 in -[_NSZombie retain] ()
#1 0x9396c690 in -[_NSModelObservingTracker setIndexReferenceModelObjectArr
ay:clearAllModelObjectObserving:] ()
#2 0x93980c54 in -[NSSelectionBinder _adjustObject:mode:observedController:observedKeyPath:context:editableState:adjustState:] ()
#3 0x939499a8 in -[NSValueBinder _observeValueForKeyPath:ofObject:context:] ()
#4 0x939808f4 in -[NSSelectionBinder observeValueForKeyPath:ofObject:change:context:] ()
#5 0x929fcd08 in -[NSObject(NSKeyValueObservingPrivate) _notifyObserversForKeyPath:change:] ()
#6 0x93972608 in -[NSController _notifyObserversForKeyPath:change:] ()
#7 0x9397246c in -[NSArrayController didChangeValuesForArrangedKeys:objectKeys:indexKeys:] ()
#8 0x93bf6c0c in -[NSArrayController _insertObject:atArrangedObjectIndex:objectHandler:] ()
#9 0x005072e8 in _ffi_call_DARWIN () at libffi-src/src/powerpc/darwin.S:119
#10 0x00506f00 in ffi_call (cif=0x1147470, fn=0x800000, rvalue=0x1147470, avalue=0x0) at libffi-src/src/powerpc/ffi_darwin.c:396
#11 0x00513da0 in PyObjCFFI_Caller (aMeth=0x4406b0, self=0x438170, args=0x103b2f0) at Modules/objc/libffi_support.m:1293
#12 0x00526eb0 in objcsel_call (self=0x4406b0, args=0x103b2f0) at Modules/objc/selector.m:516
#13 0x986e88e0 in PyObject_Call ()
#14 0x9874835c in PyEval_GetFuncDesc ()
#15 0x98747d4c in PyEval_GetFuncDesc ()
#16 0x98745414 in PyEval_EvalCode ()
#17 0x987465e4 in PyEval_EvalCodeEx ()
#18 0x986fd530 in PyFunction_SetClosure ()
#19 0x986e88e0 in PyObject_Call ()
#20 0x00527f88 in pysel_call (self=0x40dbe0, args=0x0, kwargs=0x410cf0) at Modules/objc/selector.m:941
#21 0x986e88e0 in PyObject_Call ()
#22 0x00510958 in method_stub (cif=0x1147470, resp=0xbfffc510, args=0xbfffc440, _userdata=0x0) at Modules/objc/libffi_support.m:479
#23 0x00507104 in ffi_closure_helper_DARWIN (closure=0x1147470, rvalue=0xbfffc510, pgr=0x1147470, pfr=0x0) at libffi-src/src/powerpc/ffi_darwin.c:699
#24 0x005073b4 in _ffi_closure_ASM () at darwin_closure.S:92
#25 0x937c1270 in -[NSApplication sendAction:to:from:] ()
#26 0x937c11a4 in -[NSControl sendAction:to:] ()
#27 0x937c1084 in -[NSCell _sendActionFrom:] ()
#28 0x937db09c in -[NSCell trackMouse:inRect:ofView:untilMouseUp:] ()
#29 0x937dac84 in -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] ()
#30 0x937da6a8 in -[NSControl mouseDown:] ()
#31 0x9377beb0 in -[NSWindow sendEvent:] ()
#32 0x93724ef4 in -[NSApplication sendEvent:] ()
#33 0x9371c330 in -[NSApplication run] ()
#34 0x9380ce68 in NSApplicationMain ()
#35 0x010496f4 in objc_NSApplicationMain (self=0x1147470, args=0x90ab1aa0, kwds=0x1147470) at Modules/AppKit/_AppKit.m:129
#36 0x98747c5c in PyEval_GetFuncDesc ()
#37 0x98745414 in PyEval_EvalCode ()
#38 0x987465e4 in PyEval_EvalCodeEx ()
#39 0x98747f90 in PyEval_GetFuncDesc ()
#40 0x98747d34 in PyEval_GetFuncDesc ()
#41 0x98745414 in PyEval_EvalCode ()
#42 0x987465e4 in PyEval_EvalCodeEx ()
#43 0x98742eb8 in PyEval_EvalCode ()
#44 0x9876708c in PyRun_FileExFlags ()
#45 0x9873c210 in _PyUnicodeUCS2_IsAlpha ()
#46 0x98747c5c in PyEval_GetFuncDesc ()
#47 0x98745414 in PyEval_EvalCode ()
#48 0x987465e4 in PyEval_EvalCodeEx ()
#49 0x98747f90 in PyEval_GetFuncDesc ()
#50 0x98747d34 in PyEval_GetFuncDesc ()
#51 0x98745414 in PyEval_EvalCode ()
#52 0x987465e4 in PyEval_EvalCodeEx ()
#53 0x98742eb8 in PyEval_EvalCode ()
#54 0x9876708c in PyRun_FileExFlags ()
#55 0x987660b0 in PyRun_SimpleFileExFlags ()
#56 0x00003db0 in ?? ()
#57 0x00003e50 in ?? ()
#58 0x00002414 in ?? ()
#59 0x8fe01048 in __dyld__dyld_start ()

And here's the trace output in the console when the app just pukes running outside of gdb

Stack trace (most recent call last):
0x00002414 (in Trigger)
0x00003e50 (in Trigger)
0x00003db0 (in Trigger)
_PyRun_SimpleFileExFlags (in Python)
_PyRun_FileExFlags (in Python)
_PyEval_EvalCode (in Python)
_PyEval_EvalCodeEx (in Python)
_PyEval_EvalCode (in Python)
_PyEval_GetFuncDesc (in Python)
_PyEval_GetFuncDesc (in Python)
_PyEval_EvalCodeEx (in Python)
_PyEval_EvalCode (in Python)
_PyEval_GetFuncDesc (in Python)
__PyUnicodeUCS2_IsAlpha (in Python)
_PyRun_FileExFlags (in Python)
_PyEval_EvalCode (in Python)
_PyEval_EvalCodeEx (in Python)
_PyEval_EvalCode (in Python)
_PyEval_GetFuncDesc (in Python)
_PyEval_GetFuncDesc (in Python)
_PyEval_EvalCodeEx (in Python)
_PyEval_EvalCode (in Python)
_PyEval_GetFuncDesc (in Python)
_objc_NSApplicationMain (in _AppKit.so) (_AppKit.m:129)
_NSApplicationMain (in AppKit)
-[NSApplication run] (in AppKit)
-[NSApplication sendEvent:] (in AppKit)
-[NSWindow sendEvent:] (in AppKit)
-[NSControl mouseDown:] (in AppKit)
-[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] (in AppKit)
-[NSCell trackMouse:inRect:ofView:untilMouseUp:] (in AppKit)
-[NSCell _sendActionFrom:] (in AppKit)
-[NSControl sendAction:to:] (in AppKit)
-[NSApplication sendAction:to:from:] (in AppKit)
_ffi_closure_ASM (in _objc.so) (darwin_closure.S:95)
_ffi_closure_helper_DARWIN (in _objc.so) (ffi_darwin.c:703)
_method_stub (in _objc.so) (libffi_support.m:479)
_PyObject_Call (in Python)
_pysel_call (in _objc.so) (selector.m:941)
_PyObject_Call (in Python)
_PyFunction_SetClosure (in Python)
_PyEval_EvalCodeEx (in Python)
_PyEval_EvalCode (in Python)
_PyEval_GetFuncDesc (in Python)
_PyEval_GetFuncDesc (in Python)
_PyObject_Call (in Python)
_objcsel_call (in _objc.so) (selector.m:517)
_PyObjCFFI_Caller (in _objc.so) (libffi_support.m:1300)
_ffi_call (in _objc.so) (ffi_darwin.c:404)
_ffi_call_DARWIN (in _objc.so) (darwin.S:121)
-[NSArrayController _insertObject:atArrangedObjectIndex:objectHandler:] (in AppKit)
-[NSArrayController didChangeValuesForArrangedKeys:objectKeys:indexKeys:] (in AppKit)
-[NSController _notifyObserversForKeyPath:change:] (in AppKit)
-[NSObject(NSKeyValueObservingPrivate) _notifyObserversForKeyPath:change:] (in Foundation)
-[NSSelectionBinder observeValueForKeyPath:ofObject:change:context:] (in AppKit)
-[NSValueBinder _observeValueForKeyPath:ofObject:context:] (in AppKit)
-[NSSelectionBinder _adjustObject:mode:observedController:observedKeyPath:context:editableState:adjustState:] (in AppKit)
-[_NSModelObservingTracker setIndexReferenceModelObjectArray:clearAllModelObjectObserving:] (in AppKit)
-[_NSZombie retain] (in Foundation)
+[NSException raise:format:] (in Foundation)
_NSExceptionHandlerExceptionRaiser (in ExceptionHandling)
2006-04-09 16:13:52.454 Trigger[762] NSGenericException - *** Selector 'retain' sent to dealloced instance 0x1147be0 of class OC_PythonArray.
Break at '-[_NSZombie retain]' to debug.

April 08, 2006

More coffee physics... or don't knock it 'til you try it.

You know you're a caffeine fiend when you make your morning oatmeal with hot coffee instead of water or milk.