[IronPython] IronPython 2.6.1 & ctypes, C library, and debugging with VS2008
TP
wingusr at gmail.com
Mon Jul 19 09:18:42 CEST 2010
On Sun, Jul 18, 2010 at 10:47 PM, TP <wingusr at gmail.com> wrote:
> On Sun, Jul 18, 2010 at 2:38 PM, Dino Viehland <dinov at microsoft.com> wrote:
>> TP wrote:
>>> I'm using IronPython 2.6.1 for Net 2.0 and VS2008 on Windows XP SP3.
>>>
>>> I have a python script that lets me access the Leptonica Image
>>> Processing C library (http://leptonica.com/) using the ctypes module.
>>>
>>> Everything seems to work fine with cpython 2.6. I can correctly load
>>> leptonlib.dll, create leptonica PIX images, manipulate them, and even
>>> display them using PyQT. I can use VS2008 to put breakpoints on
>>> leptonica C functions and step through the C code in the VS2008
>>> debugger.
>>>
>>> I thought I'd give IronPython a try since it also has ctypes, but I
>>> ran into a number of problems.
>>>
>>> The leptonlib.dll must be loading since I am able to correctly get
>>> back the library's version string. That is the following works:
>>>
>>> _getLeptonlibVersion = _leptonlib.getLeptonlibVersion
>>> _getLeptonlibVersion.restype = ctypes.c_void_p
>>> addr = _getLeptonlibVersion()
>>> version = ctypes.string_at(addr)
>>>
>>> However the following code doesn't seem to work under IronPython 2.6.1:
>>>
>>> pix = Pix(dimensions=(10,20,1))
>>>
>>> where essentially the following is called:
>>>
>>> _pix = ctypes.c_void_p()
>>> _pix.value = _leptonlib.pixCreate(width, height, depth)
>>> self._pix = _pix
>>>
>>> and I want to treat _pix as an opaque ptr I just hand back to
>>> leptonica whenever it needs it. For example:
>>>
>>> def height(self):
>>> """Get height of pix in pixels."""
>>> return _leptonlib.pixGetHeight(self._pix)
>>>
>>> Which works with cpython but returns something other than 20 with
>>> IronPython.
>>>
>>> By specifying "-X:Debug -X:FullFrames -X:Tracing leptonica.py" as the
>>> arguments to C:\Program Files\IronPython 2.6\ipy.exe I can get
>>> IronPython to correctly stop at places in my script where I put:
>>>
>>> import pdb
>>> pdb.set_trace()
>>>
>>> when I debug leptonlibd.dll with VS2008. However, my breakpoints on
>>> leptonica's C functions never seem to get hit. This works fine if I
>>> instead use python26.exe as command to launch when debugging.
>>
>> You should only need -X:Debug to get debugging under VS. pdb uses a
>> more Pythonic form of debugging but there's no support for it in VS.
>
> Let me be clear here. I am using the VS2008 Solution that I use to
> create leptonlib.dll. I am debugging that dll by right-clicking its
> project and setting its Configuration Properties | Debugging tab to:
>
> Command: C:\Program Files\IronPython 2.6\ipy.exe
> Arguments: -X:Debug -X:FullFrames -X:Tracing -i leptonica.py
> Working Directory: C:\leptonica\
>
> If I only use -X:Debug as you suggest I get the following error:
>
> Traceback (most recent call last):
> File "leptonica.py", line 458, in <module>
> File "leptonica.py", line 176, in __init__
> File "C:\Program Files\IronPython 2.6\Lib\pdb.py", line 1220, in set_trace
> AttributeError: 'module' object has no attribute '_getframe'>>>
>
> Googling, I determined that I needed to add at least -X:FullFrames,
> and by trial and error I found I also needed -X:Tracing. (I wasn't
> able to find any documentation on ipy.exe's command line switches? I
> just ran "ipy.exe -h" to dump out the short help description and took
> a wild guess at which might be useful)
>
>>>
>>> Ideally I like to have the VS Debugger stop in leptonlibd.dll's
>>> pixCreate() C function just before it returns its value so I can
>>> compare that to what I get back on the IronPython side.
>>>
>>> So how's does one use VS2008 to step through C functions in DLLs that
>>> are loaded by IronPython and the ctypes module?
>>
>> Do you have symbols (.PDB files) for leptonlibd.dll? You'll need symbols
>> to be able to step through the C code. You can still probably set a breakpoint
>> in the function because it's DLL exported - I think you can debug->new
>> breakpoint and enter leptonlibd!pixCreate. Even w/o symbols you could
>> step through the assembly.
>
> leptonlibd.dll is created with the C7 Compatible (/Z7) compiler
> switch. This embeds the debugging info directly in the DLL so no .pdb
> file is produced. It also does NOT use pre-compiled headers.
>
> I might also point out that the same exact .dll will hit breakpoints
> if debugged with python26.exe rather than ipy.exe. Perhaps since
> ipy.exe is built on top of .NET the VS2008 debugger handles any .dll's
> loaded by it differently? I know for example that the VS2008 debugger
> didn't like trying to run the IronPython for NET 4.0 version of
> ipy.exe (I gather you have to use VS2010 if you want to do that).
>
> Trying to set a breaking at leptonlibd!pixCreate didn't work.
>
>>>
>>> Secondly, am I using ctypes wrong, and does my code only work by
>>> happenstance for cpython.
>>
>> I don't see anything particularly wrong on your side and this looks like
>> a pretty simple call. I would assume the C functions are defined as:
>>
>> void* pixCreate(int width, int height, int depth);
>> int pixGetHeight(void* pixel);
>
> That's correct.
>
>>
>> I would hope we're getting all of this right as it seems like simple stuff
>> that should be tested somewhere. My first guess would be maybe we're
>> getting the calling convention wrong. Maybe it needs to be a WinDLL instead
>> of a CDLL? Maybe there's a check in the Python code for sys.platform which
>> is looking for win32 and uses WinDLL to open the DLL instead of CDLL on
>> Windows?
>
> More information on the problem:
>
> I decided to explicitly set the restype even though it's the default
> return type. I get the same exact behavior as my original code. Works
> with cpython, doesn't work with IronPython 2.6.1.
>
> If I have with the following python code:
>
> _pix = ctypes.c_void_p()
> _pixCreate = _leptonlib.pixCreate
> _pixCreate.restype = ctypes.c_int
> import pdb
> pdb.set_trace()
>
> _pix.value = _pixCreate(width, height, depth)
>
> I can do the following in the Command Window after pdb breaks into the script:
>
> > leptonica.py(180)__init__()
> -> _pix.value = _pixCreate(width, height, depth)
> (Pdb) n
> > leptonica.py(181)__init__()
> -> self._pix = _pix
> (Pdb) _pix.value
> *** AttributeError: 'cell' object has no attribute 'value'
>
> This is a bit strange since I just assigned to _pix.value.
> Investigating further:
>
> (Pdb) _pix
> <cell at 43: c_void_p object at 44>
>
> So instead of assigning to _pix.value (that is changing an attribute
> of _pix), IronPython is clobbering _pix?
>
> If I instead separately assign the result of pixCreate() to a
> temporary variable:
>
> width, height, depth = dimensions
> _pix = ctypes.c_void_p()
> _pixCreate = _leptonlib.pixCreate
> _pixCreate.restype = ctypes.c_int
> import pdb
> pdb.set_trace()
>
> addr = _pixCreate(width, height, depth)
> _pix.value = addr
>
> Once pdb stops the script:
>
> > leptonica.py(178)__init__()
> -> addr = _pixCreate(width, height, depth)
> (Pdb) n
> > leptonica.py(179)__init__()
> -> _pix.value = addr
> (Pdb) addr
> <cell at 43: int object at 44>
> (Pdb) type(addr)
> <type 'cell'>
> (Pdb) dir(addr)
> ['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__',
> '__getattribute__', '__hash__', '__init__', '__ne__', '__new__',
> '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
> '__sizeof__', '__str__', '__subclasshook__', 'cell_contents']
> (Pdb) type(addr.cell_contents)
> <type 'int'>
> (Pdb) addr.cell_contents
> 77491296
>
> So instead of returning an int, ctypes under IronPython is returning a
> "cell" in this case for some reason?
>
> Compare this to what I get with cpython. If I change the leptonlib.dll
> project Configuration Properties | Debugging tab to:
>
> Command: c:\Python26\python26.exe
> Arguments: -i leptonica.py
> Working Directory: C:\leptonica\
>
> Debugging by typing F5 (or choosing Debug > Start Debugging) without
> changing leptonica.py or leptonlibd.dll in any way, I then hit all my
> breakpoints set in leptonlibd.dll. I get warning messages about
> python.exe having stopped for each breakpoint and I just click the
> Continue button to close them. After I type F5 to get past all my C
> function breakpoints, pdb stops my script with:
>
> > leptonica.py(178)__init__()
> -> addr = _pixCreate(width, height, depth)
> (Pdb) n
> > leptonica.py(179)__init__()
> -> _pix.value = addr
> (Pdb) addr
> 19492760
> (Pdb) hex(addr)
> '0x1296f98'
>
> and 0x1296f98 matches what I see for the return value of pixCreate()
> from the VS2008 debugger.
>
> I also tried debugging my leptonica.py script by following the
> directions in the IronPython tutorial. I made a new Solution with the
> following Debugging properties:
>
> Command: C:\Program Files\IronPython 2.6\ipy.exe
> Arguments: -X:Debug -i leptonica.py
> Working Directory: C:\leptonica\
>
> I added my leptonlib.vcprog file to this solution by right-clicking it
> and choosing Add > Existing Project. I again set a breakpoint at the C
> function pixCreate().
>
> I also set various breakpoints in my leptonica.py script.
>
> Pressing F5 to Start Debugging, I now hit my leptonica.py breakpoints
> but still don't hit any C function breakpoints.
>
> From the locals window I can see that addr is:
>
> addr 0x00bf6ea0 object {int}
>
> Looking in a Memory window at "addr" I see:
>
> 0x06AAB590 79332d70 00bf6ea0 00000000 793042f4 00000001 33b4c9bc
> p-3y n¿.....ôB0y.....É´3
>
> So there's something else at "addr", but the next int is 0x00bf6ea0 again.
>
> The memory at 0x00bf6ea0 looks correct:
>
> 0x00BF6EA0 0a 00 00 00 14 00 00 00 01 00 00 00 01 00 00 00 01 00
> 00 00 00 00 ......................
> 0x00BF6EB6 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> f8 6e bf 00 ..................øn¿.
> 0x00BF6ECC fd fd fd fd 10 00 0b 00 f2 01 0c 00 80 6e bf 00 00 00
> 00 00 00 00 ýýýý....ò...€n¿.......
> 0x00BF6EE2 00 00 00 00 00 00 50 00 00 00 01 00 00 00 4d 00 00 00
> fd fd fd fd ......P.......M...ýýýý
>
> At least it looks to me like those are indeed ints for width, height &
> depth which is what a PIX starts out with.
>
> After:
>
> _pix.value = addr
>
> I see this in the locals window:
>
> _pix "ctypes.c_void_p instance" object
> {IronPython.NewTypes.IronPython.Modules.SimpleCData_4$4}
>
> In the Immediate window I get:
>
> _pix
> "ctypes.c_void_p instance"
> base {IronPython.Modules.CTypes.SimpleCData}: "ctypes.c_void_p instance"
> .class: PythonType: "c_void_p"
> .dict: null
> .slots_and_weakref: null
>
> But I am unable to figure out how to tell what the "value" of _pix is?
>
Since there seems to be something strange going on with using
IronPython, pdb & VS2008, I decided to just put in some printf's in
leptonlib, rebuild it, and forget about using debuggers.
Here's the results:
[C:\leptonica]python26 leptonica.py
pixCreate() returns 01293340
addr=01293340
_pix.value = 01293340
ctypes.addressof(_pix) = 00c78878
pixGetWidth() addr=01293340
width = 10
height = 20
depth = 1
[C:\leptonica]ipy leptonica.py
pixCreate() returns 046d4f60
addr=046d4f60
_pix.value = 046d4f60
ctypes.addressof(_pix) = 0353c698
pixGetWidth() addr=0353c698
width = 74272608
height = 1442168
depth = 131074
So despite what it looks like from pdb, IronPython is correctly
setting the ctypes.c_void_p. The problem is that when it passes the
ctypes.c_void_p back to a C function it gives the address of the
ctypes.c_void_p, instead of the ptr that it contains.
I'd still like to know:
Why can't I use VS2008 to set breakpoints on C functions in DLLs
that are loaded by IronPython and the ctypes module?
How to see the value of ctypes.c_void_p instances in the VS2008 debugger.
More information about the Ironpython-users
mailing list