A Modula-3 aware version of gdb may be used to debug Modula-3 code. It understands Modula-3 types, declarations, and expressions. It also supports threads. The usual gdb commands may be used. This document discusses three areas where special care is required: garbage collection, threads, and calling non standard procedures. While gdb tries to determine automatically the language for the current frame (procedure), you may sometimes want to explicitely set the current language:
(m3gdb) set language m3 (m3gdb) set language c
On some platforms, a VM synchronized version of the garbage collector uses SIGSEGV signals. By default, gdb stops and prints a message each time SIGSEGV is received. This behavior may be changed with the handle command. The simplest way to avoid these signals is to disable the VM synchronized garbage collector, using the @M3novm command line option:
cassis> m3gdb myprog (m3gdb) run @M3novm ...other arguments...
If you prefer to keep the VM synchronized collector while debugging, you may use:
(m3gdb) handle SIGSEGV noprint pass
However, you will then not be able to examine the protected memory regions. You may unprotect the memory with:
(m3gdb) call RTCollectorSRC.FinishVM()
You may disable the memory protection for the rest of the execution with:
(m3gdb) call RTCollectorSRC.DisableVM()
On platforms where Modula-3 uses native threads (NT), the gdb thread commands may be used (info thread, and thread) to list the threads and switch to another thread.
On other platforms, m3gdb provides threads to list the threads, and switch id to switch to another thread indicated by id. You must switch back to the thread where the program stopped before continuing the execution (i.e. the first thread listed by the threads). If the switch command is interrupted, the state of m3gdb is essentially random and it will most likely crash. The current language must be m3 for the threads and switch commands to work.
Modula-3 procedures are mapped as closely as possible into C procedures and may be called by gdb using call. Two differences exist: "large" results and nested procedures.
First, procedures that return structured values (i.e. records, arrays or sets) take an extra parameter. The last parameter is a pointer to the memory that will receive the returned result. This parameter was necessary because some C compilers return structured results by momentarily copying them into global memory. The global memory scheme works fine until it's preempted by the Modula-3 thread scheduler.
Second, nested procedures are passed an extra parameter, the ``static link''. The exact details of how that parameter are passed are system dependent. When a nested procedure is passed as a parameter, the address of the corresponding C procedure and its extra parameter are packaged into a small closure record. The address of this record is actually passed. Any call through a formal procedure parameter first checks to see whether the parameter is a closure or not and then makes the appropriate call. Likewise, assignments of formal procedure parameters to variables perform runtime checks for closures.
<*EXTERNAL*> procedures have no extra parameters. except if they return large results??