[Openmcl-devel] Debugging in Kernel in Windows vista 32 bit

Gary Byers gb at clozure.com
Sun Dec 20 12:34:29 PST 2009

On Sun, 20 Dec 2009, Seth wrote:

> Im trying to port the RDNZL library to clozure.
> However the code throws me in the low level debugger. I have wrote some test code
> that actually works and then i have intentionally put an error in so i know the
> error source. Now i would like to verify the source of error, assuming i didn't
> know it, by somehow detecting the error thrown in the c function. Of course the
> goal is to figure out why the test code i want doesn't work.
> Just for an idea, this is the c++ code:
> __declspec(dllexport) void* invokeStaticMember(const __wchar_t *methodName, void
> *type, int nargs, void *args[]) {
> 		Type ^t = safe_cast<Type ^>(static_cast<DotNetContainer
> *>(type)->getContainerObject());
>                return InvokeMember::invokeMethod(nullptr, t, methodName, nargs,
> args, static_cast<BindingFlags>
>                                                           (BindingFlags::Static
> | BindingFlags::Public));
> }
> And this is my working test code:
> (in-package :rdnzl)
> (defun %invoke-instance-member (method-name type nargs args)
>  (ccl::with-native-utf-16-cstr (mn method-name)
>    (ccl:external-call "invokeInstanceMember"
>      :address mn :address type :signed-int nargs
>      :address args :address)))

Were you intentionally showing the declaration of invokeStaticMember and code
which calls invokeInstanceMember ?  Or are those two functions equivalent in
some way ?

> (defun test-invoke-2 ()
>   (let ((obj (make-type-from-name "System.Reflection.Assembly")))
>     (%invoke-instance-member "ToString" (pointer obj) 0 (ccl::%null-ptr))))
> I introduce the error by chaing ToString to TToString.
> I would somehow like to 'catch' the error in the precompiled dll to see what
> error is thrown . All that the kernel tells me
> is that an "Exception occured while executing foreign code".
> Can anyone suggest a good way to do this? Thanks!

<http://trac.clozure.com/ccl/wiki/CclUnderGdb> is probably a bit Unix-centric;
it describes ways of attaching GDB to a running CCL.  (I've found that it
sometimes works better to attach GDB to a running CCL than it does to run
CCL under GDB; in the latter case, both CCL and GDB wind up trying to read
from the same standard input device, with no way of arbitrating ownership of
that device and staying out of each other's way.)


1) start the lisp and load the RDNZL code and its .dll(s).  Note the
process ID of the lisp process:

? (ccl::getpid)
PID               ; an integer, likely a fairly small one.

You might also wish to try to determine the address of "invokeInstanceMember";
there are a few ways to do this.  One way is to note the address in the
printed representation of the "external entry point" used to refer to that
foreign function:

? (ccl:external "invokeInstanceMember")

The address of interest will be a hex number in parens in that printed

2) in another terminal window, start GDB

> gdb /path/to/wx86cl.exe

What syntax you have to use for that path depends on what version of GDB
you're using and how you're running it (cygwin, mingw/msys, ...)

3) "source" (as a verb) a .gdbinit file that (mostly) tells GDB to pass
certain exceptions/signals to the application:

(gdb) source lisp-kernel/win32/.gdbinit

4) Attach GDB to the running wx86cl:

(gdb) attach PID ; where PID is the integer returned by CCL::GETPID

5) Set a breakpoint at invokeInstanceMember.  You can try:

(gdb) br *invokeInstanceMember

Whether that works or not depends on how well GDB does at looking up
symbols in .dlls that were likely compiled with an MS C compiler.  The
leading asterisk in this case means "set the breakpoint at the address
of the function, without skipping over a C function prolog"; doing that
should enable you to see the incoming arguments as the caller passed them.

If GDB can't make sense out of that symbol, you can also do:

(gdb) br *0x12345678 ; or whatever the address of invokeInstanceMember is

In this case, the leading asterisk means "interpret the following number 
as an address, not as a line number."

6) Let the lisp run again.

(gdb) continue

7) In CCL, call your test case.  This should cause GDB to stop at the
breakpoint set above.  Debug away ...

> Also, as a side note, is there any way to prevent clozure going into this kernel
> by catching the error?

You're getting thrown into the kernel debugger because of a machine-level
exception (likely some sort of memory fault.)

If you get a memory fault while running lisp code - perhaps code like:

(defun foo (x)
   (declare (optimize (speed 3) (safety 0))) ; what could go wrong ?
   (cdr x))

(foo 17) ; oh right, that could ...

- the fault will be treated as a lisp error.  Odds are pretty good
that you can recover from that error and (in this case) either fix FOO
to be less aggressive or fix its callers to be more cautious.
Recovery isn't guaranteed - the fault may have been the result of a
very buggy function scribbling over memory until it eventually did
an invalid (rather than merely incorrect) memory access - but in
practice it often "works" to treat memory faults in lisp code as
lisp-level errors.

If a similar kind of memory fault occurs while executing foreign (C,
C++, ...) code, it's a lot less clear that simply treating that
situation as a lisp-level error would likely "work" - we don't know
how well that C code can deal with being interrupted in that way -
we don't know what state it's changed or what it depends on.  I can
see the utility of a kernel debugger command that says "cross your
fingers and signal a lisp error" here, but it's not at all clear
that crossing one's fingers (and running a real risk of causing
some other fault which obscures the original error) is useful default

> ---- Msg sent via WebMail
> _______________________________________________
> Openmcl-devel mailing list
> Openmcl-devel at clozure.com
> http://clozure.com/mailman/listinfo/openmcl-devel

More information about the Openmcl-devel mailing list