Table of Contents
m3gdb is a modified version of the well-known gdb debugger, with added support for the Modula-3 programming language. Much of the function of m3gdb is the same as gdb, and this article makes no attempt to duplicate information found in existing gdb documentation . Instead, it documents properties of m3gdb that add to or differ from gdb. The reader is assumed to be familiar the gdb documentation, or able to consult it as necessary. Most of the commands, command line options, environment variables, etc. of m3gdb are the same as for gdb. The differences lie in the syntax and semantics of expressions, linespecs, etc. and in the output m3gdb produces, when Modula-3 code is involved.
Throughout, the term "gdb" will be used in statements that apply only to unmodified gdb. The term "m3gdb" will be used in statements that are specific to m3gdb. The term "(m3)gdb" will be used to state properties that m3gdb inherits from gdb, and thus are the same in both debuggers.
gdb is a multi-language debugger. In deriving (m3)gdb from gdb, none of gdb's function is removed. Only new support for Modula-3 is added. Thus, (m3)gdb is also a multi-language debugger, with one additional supported programming language. (m3)gdb further supports debugging mixed-language programs, where modules written in different languages are linked together.
m3gdb supports code compiled by the SRC, PM3, EZM3, and CM3 Modula-3 compilers. A single m3gdb executable dynamically detects and adapts to code compiled by any of these compilers. The SRC, PM3, and EZM3 compilers differ very little, as far as m3gdb is concerned, so the phrase "PM3 et. al." will be used in statements that apply equally to any of these three Modula-3 compilers. More information on the various compilers and other language implementation alternatives and be found in this section.
      Despite its support for all four Modula-3 compilers, the current, maintained
      version of m3gdb is kept only in the CM3 source repositories.  
      Within the repository it is found at 
      cm3/m3-sys/m3gdb.  
      It is entirely written in C, so a working Modula-3 compiler is not 
      required to build it.  
      However, it is integrated into CM3, 
      and this integration uses m3makefiles 
      and the Modula-3 build system.   
      Thus, the easiest way to build it is, with a working installed CM3,
      go into subdirectory cm3/scripts and execute 
      ./do-cm3-m3gdb.sh buildship.  
      This will build and install m3gdb in the normal CM3 
      bin directory, usually 
      /usr/local/cm3/bin, where 
      the installed executable will be named m3gdb.
    
      This will go through the usual configure process, which will build
      a debugger that both executes on and debugs programs on the machine
      the build process executes on.  
      Building by this method will always repeat the configuration
      process, which can be annoyingly time-consuming if you are doing
      development work on m3gdb and have only modified a source
      file or two.  
      Once the configure step has been done on a given machine, 
      it is safe to disable it for future recompiles on the same machine.  
      You can do this by uncommenting the line
      %quick = 1, found near the top of 
      cm3/m3-sys/m3gdb/src/m3makefile.  
      Note that this is quake code, where the
      "%" is the comment-start character.  
    
      To build m3gdb without using a Modula-3 compiler, go in to 
      cm3/m3-sys/m3gdb and execute  
      ./configure and make.
      This is the usual C build process.  The compiled executable
      will then be found in cm3/m3-sys/m3gdb/gdb/gdb,
      from whence you can move or copy it as desired.
    
      (m3)gdb has a source file ada-lex.c,
      that is mechanically generated from ada-lex.l,
      by flex.  
      Normally, the CM3 distribution will contain an up-to-date 
      ada-lex.c, but if not, you may need to 
      have flex installed to build m3gdb.  
      There are also several *.y
      files that need bison to regenerate their
      corresponding *.c files, following the same
      pattern.    
    
      In order to be able to debug variables of Modula-3 types 
      INTEGER and LONGINT,
      m3gdb needs to be compiled by a C compiler that has an
      integer size at least as wide as each of these Modula-3 types. 
    
      As with all debuggers, (m3)gdb requires that code to be debugged
      be compiled with certain options that ask the compiler to insert 
      debug information in the object modules and executable.  
      (m3)gdb needs this information to 
      know things like the names, memory locations, and types of variables.
      Without such information, (m3)gdb can still function, but only
      at the machine  instruction level,
      or in very limited ways at the source code level. 
      m3gdb recognizes only the 
      stabs or stabs+ debug
      information format.  In CM3, the latter is required for full
      m3gdb function.     
    
      To check whether an object or executable file has the right
      kind of debug information,
      execute objdump -G <filename>|head.  
      The file contains the needed debug information iff you
      see the line "Contents of .stab section:". 
    
      By default, the CM3 distribution is set up to compile with 
      debug information, at least on some platforms.  
      There are mechanisms for specifying either on the cm3
      command line or in the m3makefile, that it should
      be produced, but these default to on, and there does not seem
      to be a way to turn them off.  
    
      However, debug information can be turned off or on in 
      another way by omitting or adding the line 
      if debug     args += "-gstabs+"  end  
      in the quake
      procedure named m3_backend.  
      This is found in the CM3 configuration file, usually located at 
      /usr/local/cm3/bin/cm3.cfg.
    
For example, to enable debug information, it should look someting like:
        proc m3_backend (source, object, optimize, debug) is
          local args = [ "-quiet", source, "-o", object, "-fPIC", "-m32", "-fno-reorder-blocks" ]
          if optimize  args += "-O3"  end
          if debug     args += "-gstabs+"  end
          if M3_PROFILING args += "-p" end
          return try_exec (m3back, args)
        end
      This line has been missing in the distributed configuration file for some targets, at some times in the past few years.
      By default, PM3 et. al. produce debug information.
      If your installation is not giving debug information, add the line
      option("debuginfo","T") to your 
      m3makefile.  
      To suppress generation of debug information, add the line
      option("debuginfo","") to your 
      m3makefile.  
    
        In C, every expression is also a statement and may return a
        result and/or have side-effects.  
        These semantics are reflected in gdb, which was first a C debugger.  
        In particular, the print command accepts an argument that is an 
        expression, which it both evaluates, printing the result, and carries out 
        its side-effects on the debugee program.  
        The expression could have no result,
        if its type is void, in which case, the 
        print command displays (void).
        The user can execute a C assignment or call by typing it as
        the argument to the print command. 
        This facility is, in effect, an interpreter for a significant
        subset of the language, implemented by gdb.    
      
        Many of the Modula-3-specific extensions that m3gdb provides are
        interpretation capabilities for Modula-3. 
        m3gdb follows the C-like pattern for the print
        command by allowing its argument to be either an expression, an
        assignment statement, or a call statement.  
        Note that an expression could also be a call on a function 
        procedure or method.  
        If the argument is a Modula-3 expression, the print command
        evaluates it and prints the result.  
        If the expression contains a function call, there could also 
        be side-effects as well.  
        If the argument is a statement, the command executes the statement and
        displays (void).  
        Section Expressions describes 
        the expressions m3gdb can evaluate.  
      
        When the main program is written in Modula-3, 
        the m3gdb start command will
        execute all compiler-generated initializations and all
        module initialization bodies in the 
        runtime system, which
        is in library libm3core 
        and always used when there is Modula-3 code.  
        (but see this note).   
        Execution will stop before any of the other module initialization
        blocks. 
      
        For code compiled by PM3 et. al., m3gdb accomplishes this by setting 
        a breakpoint in procedure RunMainBodies, 
        and the stop after the start command will
        appear as hitting this breakpoint. 
        RunMainBodies is located in library 
        libm3core, and if it is dynamically linked, 
        it may not have been loaded at the time you type the 
        start command, thus requiring you to go 
        through the usual ritual of allowing the breakpoint to be made pending,
        until the containing library is loaded.  
        Answer y to the question.
        The breakpoint will be resolved automatically.   
      
        For CM3-compiled code, m3gdb accomplishes this by setting 
        a breakpoint in Main.i3, and the stop 
        after the start command will
        appear as hitting this breakpoint. 
      
(m3)gdb has commands, e.g., the break command, that take an argument called a linespec. A linespec is used to denote an executable place in the code. There is overlap beween an expression and a linespec, because either can refer to a module, interface, procedure, or (anonymous) block. In a linespec, a procedure or block directly denotes its first executable statement, while any of the four can be used as qualifier in path eventually leading to a procedure or block. In an expression, a procedure can be named in a call, actual parameter, or as a procedure constant, an interface or module can similarly be used as a qualifier, leading to a procedure.
However, there are significant differences in (m3)gdb's syntax and semantics of linespecs and expressions. An expression is more general in one respect in that it can denote a variable, formal parameter, type, field, method, or procedure. But an expression needs to have a context where the debugee program is stopped, in order to get, e.g., values of variables, and, more fundamentally, to imply which language the expression is to be interpreted in.
      In contrast, a complete linespec can only denote an 
      executable location.  
      While this cannot be a variable, formal, type, field, or
      method, it must be possible to make sense of it independent of
      any execution context.  
      This means it could be a specification in the syntax of any of the 
      languages supported by (m3)gdb.  
      It could also have the form 
      <sourceFileName>:<lineNumber>, 
      where <sourceFileName> could contain dots.  
      So (m3)gdb's parsing and analysis of expressions and linespecs 
      is quite different.
      Nonetheless, m3gdb attempts to make them behave the same, for
      cases that can occur in either kind of denotation.  
    
        A linespec with the form 
          <sourceFileName>:<lineNumber>
        denotes the specified line number of the source file.  This
        is no different from other gdb-supported languages.
        If this line is not an executable place, (m3)gdb will use
        a nearby line that is.   
      
A linespec can take the form of a list of components, each of which is an identifier or a decimal number, separated by dots, with embedded white space allowed between the tokens. This is a kind of fully-qualified path to a procedure or block. The first component must be an identifier and must denote an interface, module, or procedure. A subsequent identifier denotes a procedure by that name, declared local to the unit denoted by the prefix. A number n denotes the n-th anonymous block nested immediately inside the unit denoted by the prefix. This system allows specification of any procedure or block.
If the debugee program is running but stopped, and thus has an execution context, m3gdb first tries to interpret the first identifier in that context, using the scope rules of Modula-3. If that fails, m3gdb next tries to interpret the first identifier as an interface name, looking in the entire link closure of the program. If that fails, m3gdb finally tries to interpret the first identifier as a module name, again looking in the entire link closure of the program. If the first identifier refers to a module in a not-yet-loaded, dynamically linked library, this will fail, and (m3)gdb will try to find another way to interpret the linespec.
A subsequent identifier that denotes anything other than a procedure is a failed attempt at interpreting the linespec. Likewise, a block number that would denote a nonexistent block is a failure. Although a procedure can be referred to using an interface as prefix, any further components of the linespec are interpreted relative to the body of the procedure, i.e., within the module that exports the procedure. A procedure within a module will be found if it is either explicitly declared in the module or in an exported interface of the module. This means that, when the exporting module has a different name from an interface, a procedure can usually be referenced using either the interface name or the module name as prefix.
        The initialization block of a module can
        be specified as either the module name alone, or with the form
        <moduleName>.1, i.e., the first 
        (and only, in this case) block inside the module.  
        As a prefix of a longer linespec (i.e., to specify some 
        procedure or block inside the module initialization block), 
        the latter form is required, because, for example, 
        <moduleName>.<procedureName>;
        denotes a procedure declared local to the module, not to 
        the module's initialization block.    
        See this note about
        hitting breakpoints specified in this way.
      
m3gdb is inconsistent about what it does when an interpretation attempt fails. Sometimes it tries the next way of looking up the first identifier as Modula-3 code. This usually happens when the failure occurs within the first two components. Sometimes it abandons trying to interpret the entire linespec as Modula-3 code, but allows gdb to try other ways to interpret it, in other languages. This usually happens when the first identifier can't be found and when the component list is ill-formed. Sometimes it displays an error message and gives up altogether. This happens for bad components after the second. This inconsistency is considered a bug, but it is not obvious what the best semantics are.
All the Modula-3 compilers emit mangled linker names for procedures, and such a name can be used in a linespec as an alternative way to identify a procedure. The mangled name for any procedure is similar to the component list linespecs above, with each dot replaced by two underscores. The entire name is a single linker name, so no embedded white space is allowed. The first component is always the module the procedure's body is declared in. The block numbers have no leading zeros. It is possible for a programmer to spoof such a name by an identifier with double underscores, causing confusion to m3gdb.
The following list gives Modula-3 code constructs that are supported, to at least some degree, in m3gdb expressions. Where m3gdb semantics are not identical to Modula-3 semantics, subsequent subsections describe the differences. All of these constructs are treated as expressions by m3gdb. but some are statements in Modula-3.
          
            DIV, MOD, 
            *, /, 
            binary +, and binary -
           operators.
        
          
            ABS, 
            unary +, and unary -
           operators.
        
          Boolean operators 
          AND, OR, and
          NOT.
        
          The MIN and MAX functions.
        
          The 
            concatenation operator &
          .
        
          The assignment statement :=.
          See notes on assignability of
          arrays and
          procedures.  
        
          The ORD and VAL functions.
        
          The CIELING, FLOOR, 
          ROUND and FLOAT functions. 
        
          The 
            FIRST, LAST, 
            and NUMBER
           functions.
        
          The TYPECODE 
          function.
        
          The LOOPHOLE.
          function.
        
          The 
            ADRSIZE, BITSIZE, 
            and BYTESIZE
           functions.
        
          The ADR function. 
        
m3gdb generally follows Modula-3's rules for looking up an unqualified identifier, with some exceptions.
          The debug symbol data emitted by the compilers contains no information
          about identifiers declared in a CONST
          or EXCEPTION declaration, so you can't refer to
          these in an m3gdb expression.  
          (But you can refer to 
           values of enumeration types.)   
          This can further change the semantics of identifier lookup.  
          For example, in true Modula-3 code, there could be a reference to a 
          named constant that is declared in some inner scope.  
          In an m3gdb expression, the same identifier might end up denoting 
          an entirely different declaration by the same name, in some outer 
          scope.
          Since m3gdb doesn't know the constant exists, it will
          find the identifier in the outer scope. 
        
When a normal Modula-3 scope lookup of an identifier fails, m3gdb looks for an interface or module by that name, in the entire link closure of the program, excluding any not-yet-loaded dynamically linked libraries. The name of an interface or module, by itself, has no meaning in an m3gdb expression, but it can be followed by a dot and used to name a procedure, variable, or type declared in the interface or module. Identifiers known in a module via exported interfaces can be named in this way, in addition to those declared directly in the module. If there is both an interface and a module by the same name, the interface is searched first, but a procedure named in that way is treated as referring to the procedure body.
If, perversely, there were a module that did not export a same-named interface and both contained different declarations of the same identifier, this lookup order would mean you could not name the meaning declared in the module, unless the program context were somewhere inside the module.
For PM3 et. al., m3gdb fully supports references to variables that are declared in some scope outer to the referencing context.
For CM3, the debug information for following static links is inadequate. So long as nested procedures are called only as procedure constants, it should work correctly. If a nested procedure is called through a formal parameter, m3gdb might find the wrong instance of a statically containing procedure, when accessing the containing procedure's variables nonlocally. For now, m3gdb warns whenever accessing variables nonlocally. Also, see this note.
This problem does not apply to fully global variables, since they do not require the static link mechanism to address them.
Dot selections in m3gdb follow most of Modula-3's rules. You can select:
You can not select a constant or exception of an interface or module. You can not select a method name of an object type.
m3gdb displays the value of a procedure in both of two ways: a qualified path as in a linespec, and a source file and line number.
A procedure can be referred to in an m3gdb expression, either as part of a call, to pass the procedure (constant) as an actual parameter, or to assign it to a variable.
m3gdb does not check assignability of two procedure types. This is relevant for assignments and passing parameters. If both are procedure types, it assumes they are assignable. It warns when it makes this liberal assumption. Bad things will almost certainly happen if you abuse this.
        A type can be referred to in an m3gdb expression.  
        The primary use is as parameter to builtin functions such as 
        LOOPHOLE, VAL, etc.
        Only named types work, either builtin or declared in a 
        TYPE declaration.
        m3gdb does not recognize Modula-3 type constructors. 
      
m3gdb displays the value of a named type as a qualified path, as in a linespec, but ending with a type name.
        m3gdb displays values of type TEXT 
        and accepts them in expressions.  
        It supports both the form used in PM3 et. al. and the form used in CM3.
        If the program was compiled by CM3, it will handle wide 
        TEXT literals and also the type 
        WIDECHAR and its literals.  
      
        Normally, m3gdb displays TEXT
        values in Modula-3 lexical syntax, with double
        quotes, escape sequences, and, if appropriate, a leading 
        'W'. 
        However, the /k option in a print command will 
        cause it instead to display the TEXT 
        value's internal data structure.  
        For PM3 et. al., this is a traced reference to an open array of 
        characters.
        For CM3, this will be one of the several object subtypes of 
        TEXT.  
        m3gdb properly recognizes and displays values of CM3 type 
        TextLiteral.T.
        Here, it takes the length of the text from the appropriate field, 
        instead of from the declared type, which contains a fixed array whose
        length is almost always far too long. 
      
Even without specifying the /k option, if you happen to know what the internal representation is, you can apply appropriate operators to it, e.g., dereferencing, field selection, subscripting, or even method calls.
        There are some cases where, in order to evaluate/execute a user-typed
        expression, m3gdb has to actually allocate an object in the heap
        of the debugee program.  
        Examples are assigning a user-typed TEXT value
        to a variable or passing it as an actual parameter.
        This can happen unexpectedly if the expression in the m3gdb command
        contains the Modula-3 concatenation operator &, 
        which m3gdb evaluates by calling Text.Cat 
        in the debugee process.
        This will alter the debugee's execution environment it a subtle way.  
        Usually this will not matter, and the garbage collector will eventually 
        collect such objects after they become inaccessible.  
        Such operations will not work if you are only debugging a 
        core file and don't have an executing 
        debugee program.    
      
        If you type print "ABC" & "DEF", 
        m3gdb will execute three calls in the target program.  
        Two on Text.FromChars to get the 
        TEXT values allocated and one on 
        Text.Cat to do the concatenation.
        All three TEXT strings will be allocated
        in the debugee program's traced heap, 
        but will immediately become garbage, available for collection.
      
        m3gdb tolerates application of a redundant dereferencing operator
        ^ to a value of an object type,
        with a warning. 
      
m3gdb always treats a heap object as having its allocated type, not the static type of the expression used to refer to it. This allows you to select fields, etc. of the actual object.
        A LOOPHOLE applied to a heap object is an
        exception.  
        This would allow you to access a field or method of a supertype
        that was hidden by a different but same-named field or method
        in the allocated type. 
      
m3gdb does not support subscripting on non-byte-aligned, bitpacked arrays.
m3gdb does not support array constructors.
m3gdb does not allow assignability of references to non-equal array types. This is relevant for assignments and passing parameters. In Modula-3, some such combinations are assignable. Sorry.
        For ordinal types, m3gdb can't distinguish a VAR
        parameter from a READONLY parameter, given the
        information available from any of the compilers.  
        In this case, it assumes VAR, and requires 
        the actual and formal to have identical types.  
        This assumption makes VAR mode
        work (i.e., the actual can be changed by the called procedure), but
        means m3gdb's type rule is overly strict for 
        READONLY.  
      
        In the latter case, if the actual is
        assignable but not identical and you really need to pass it, you 
        will have to use a LOOPHOLE on the actual 
        parameter in the call.  
        On the other hand, if you use the LOOPHOLE 
        and the mode is really VAR, bad things could happen. 
      
        For all other classes of types, either m3gdb has a way to 
        distinguish VAR from READONLY, 
        or it doesn't matter. 
      
        m3gdb's liberal type rules allow the integer binary 
        (i.e., two-operand) arithmetic operations
        to be performed on any subrange type and any reference type,
        as well as INTEGER, LONGINT, 
        CARDINAL, LONGCARD, 
        and mixtures thereof. 
      
        ABS and unary - 
        can only be applied to an INTEGER, 
        LONGINT, or floating operand.
        They won't even work on a PACKED
        or a formal parameter passed by reference.   
        Since it is semantically an identity, m3gdb just ignores 
        unary + without even bothering to type check it. 
      
        Builtin functions 
        FIRST, LAST, 
        and NUMBER
        work on ordinal and array types only. 
        They do not work on array values
        or on floating types or values.
      
        TYPECODE works on non-NIL
        values that have traced reference types.
        It does not work on types, 
        nor on the value NIL. 
      
        LOOPHOLE works as in Modula-3, except it cannot be
        used to convert to an open array type.
      
        ADRSIZE, BITSIZE, 
        and BYTESIZE work as in Modula-3, except they
        cannot be applied to an open array value. 
      
m3gdb supports code compiled by the SRC, PM3, EZM3, and CM3 Modula-3 compilers. A single m3gdb executable dynamically detects and adapts to code compiled by any of these compilers.
Programs that link together Modula-3 code produced by a mixture of CM3 and PM3 et. al. compilers are likely to confuse m3gdb's detection of what compiler was used. These compilers have significant differences in their runtime organization, and m3gdb assumes there is not a mixture. Any such problem behaviour can even depend on the order in which dynamically-linked libraries are brought in. In fact, mixing code like this is likely to cause other problems, even without m3gdb's involvement.
There are two different code generators in use by the various compilers. One is derived from the well-known gcc compiler, with modest modifications to support Modula-3. It inherits much of gcc's repertoire of supported targets and its range of optimization options. For PM3 et. al., it is derived from gcc 2.7.2. For CM3, it is derived from gcc 4.3.0. The other code generator is an i386-specific code generator, written in Modula-3 and designed for fast compilation.
      There are three different thread implementations for Modula-3 threads. 
      One uses setjmp and longjmp
      and is implemented entirely within the runtime library 
      libm3core.
      It subschedules the process thread that is provided by the 
      operating system, among the Modula-3 threads.  
      In recent years, it has been undermined by security-motivated changes
      in setjmp and longjmp.  
      Only a few platforms have an updated version.  
      The second is much newer and uses the library 
      libpthread.
      It integrates scheduling of Modula-3 threads with other threads.  
      It also is adapted to multi-core and multi-chip SMP systems. 
      The third uses Windows native threads and is used only on
      Windows platforms. 
    
There are two different garbage collectors. Both are capable of incremental collection, i.e., running collection activity in a parallel thread to the running program, thus avoiding unexpected pauses in execution. The older, virtual-memory-synchronized collector uses virtual memory to detect heap changes that would otherwise undermine the correctness of the collector. The newer, compiler-assisted collector uses compiler-inserted notifications for the same purpose.
m3gdb has a new command "Info Modula-3". This will tell you which compiler and which code generator were used to compile the debugee program and which threads implementation and which garbage collector are in use. In case you have trouble remembering which spelling of the language name to use, it accepts the cartesian product of "Modula" spelled out or abbreviated with a single "M", lowercase/uppercase "M", and with/without the hyphen. A mixture of implementations will confuse it. It may be unable to get some of the information before the runtime system has been initialized.
 
        The virtual-memory-synchronized garbage collector works by
        asking the operating system to artificially hardware-protect certain
        memory areas from access and to notify it when such and access occurs.
        The notification is through a segment fault that the garbage
        collector catches and handles.  
        Unfortunately, m3gdb can't distinguish this artificial segment
        fault from a normal one, resulting in numerous false stops of 
        the debugee program.
        m3gdb prevents this by automatically disabling incremental
        collection when debugging with this collector.  
        This is the equivalent of manually adding @M3novm 
        to the command line arguments, which you can also do redundantly 
        and harmlessly.
        You can reenable incremental collection by typing the command
        "print RTCollectorSRC.EnableVM()", 
        after the  
        runtime system has been initialized.
      
        The libpthread thread implementation uses 
        signal SIG64 internally.  
        By default, (m3)gdb stops when this signal is received,
        resulting in numerous false stops.  
        When this thread implementation is in use, m3gdb automatically 
        executes the command
        "handle SIG64 nostop noprint pass", 
        before the debugee program starts to run.   
        This causes m3gdb to silently pass this signal to the program,
        preventing the false stops.  
        If you want to reverse this, for example, to debug this thread
        implementation, type the command 
        "handle SIG64 stop print pass".
      
Some debugging commands can fail if executed too early, before certain initializations have happened. Understanding initialization can matter to you if you want to debug module body code or the runtime system itself, or try to execute calls in m3gdb commands before everything has been initialized.
m3gdb uses addresses found in debug information in executable files to address global variables and procedures. On some targets, executable code uses a different mechanism that addresses global variables and procedures indirectly, using pointers that are also stored in global locations. These pointers are initialized by compiler/linker-generated machine code, and this happens during program startup.
You can access a global variable with an m3gdb command any time, although its value may not yet be initialized. However, if you call a global procedure using an m3gdb command, and it or any other procedure it calls, directly or indirectly, accesses a global variable, it may use one of these pointers. If the pointer has not yet been initialized, this will almost certainly cause your program to suffer a segment fault that would never happen if you only executed compiler-generated code.
Other problems of more varied nature can also occur if the module body code, written by the Modula-3 programmer, has not been executed when you use an m3gdb command to call a procedure too early.
 
      Using the m3gdb 
      start command 
      will ensure that the runtime system has been initialized.
      After that, m3gdb calls on procedures that are in 
      libm3core and in the closure should be safe. 
      To use m3gdb safely to make calls in your own code, you need
      to be sure execution has proceeded at least through the first
      line of the body of the module that exports Main.
    
      If you link in a library, all the code of the entire library is loaded
      into your address space, and all the debug data is available to m3gdb,
      even for modules that are in the library but not in the 
      IMPORT/EXPORT closure of the main program.  
      This means you can call procedures in such modules and access their
      global variables from m3gdb commands.
      However, the compilers take care of initialization 
      only for modules in the closure, so such modules will
      never be initialized.  Similarly, only certain modules in   
      libm3core are initialized as part of
      the runtime system. 
    
      As an example, you can always call 
      RTTypeFP.FromFingerprint, because it
      is in libm3core.  
      And this could be a useful thing to want to do during
      a debug session (e.g., when debugging something involving pickles). 
      But RTTypeFP is not initialized as part
      of the runtime system and is usually not named in any 
      IMPORT or EXPORT
      in a typical program, and therefore is not in the closure.
    
      So this call, in an m3gdb command, will result in a
      segment fault while trying allocate a heap object of a type 
      declared in RTTypeFP.
      Even the needed type definition is present in memory, 
      but the needed pointer to it is not set up.
      To make this work, you have to put 
      IMPORT RTTypeFP; somewhere in your
      program and recompile and also postpone the m3gdb call
      until the necessary initialization has taken place.  
    
        In CM3-compiled code, if you put a breakpoint at, e.g., 
        Mod.1 rather than using a
        source code line number, you will see somewhat strange behaviour.
        The CM3 runtime system invokes module bodies multiple times.
        The compiler translates them so they do different things
        on different invocations.  
        The compiler also gives them a parameter named 
        mode, whose value, in part, determines what
        the module body code will do.   
      
        If execution stops at a breakpoint such as 
        Mod.1, and mode 
        has value 0, the programmer-written 
        Modula-3 code of the module body will not be executed during this
        invocation, and the initialization of the runtime system 
        might not have been done either.  
        This can happen more than once.   
        When mode has value 1,
        then the Modula-3 code itself is about to be executed.  
      
You can work around this by using a linespec with a source code line number in your break command.
There are many things you can do to aid debugging, just by using m3gdb commands to print and set variables and to call procedures in the runtime system.
As an example, RTTypeFP.FromFingerprint could be useful when debugging pickles themselves, or a program that uses them. However, see here for a caveat on how to use it.
In CM3, there are problems in displaying variables and formal parameters that are nonlocally accessed somewhere in the Modula-3 code, (in addition to this problem.) If a formal parameter is accessed nonlocally anywhere in the program, accessing it locally in m3gdb by the commands info arg, frame, or backtrace will display an incorrect value, while info loc will give two values for such a parameter, only one of which is correct. The print command appears to be correct in such cases, as far as tested to date.
m3gdb was originally developed as a modification to gdb, version 4.17, done at DEC's Stanford Research Center, by unknown authors. Over the years, the modifications have been moved to gdb versions 4.0.1.0, 5.3, 6.3, and 6.4, along with many enhancements, by Antony Hosking and Rodney Bates. Any additional information about history or contributors would be welcomed.
This document was originally written in October of 2008 by Rodney M. Bates, rodney.bates@wichita.edu.