This is a simple program to debug memory allocation in a program. It can also function as a very simple debugger.


Start the program by giving the command line for the program to be monitored, as follows :-

memdebug flowterm /tmp

The flowterm program will then load and start and, concurrently, a ? prompt will appear at which commands can be given to memdebug.

This works well for a graphical program, which doesn't use the terminal. For a terminal-based program, it may be easier to use memdebug's graphics mode, with the -g option :-

memdebug -g oidoc -html -d -o /tmp/libref -a

Using memdebug with the terminal has the advantage that command output can be piped (usually to less).

Entering commands

Commands to memdebug take a simple shell-like form; for example

? list MyClass#2

Output can be redirected by ending a command with >file or >>file, just like the shell. Output can also be piped to a command using the | character followed by the command, but this doesn't work if memdebug is using its graphical mode (the -g option).

Stopping the monitored program

This can be done by entering the stop command at the prompt. It happens automatically when the monitored program returns from main, or when it stops with a runtime error.

Note that if the monitored program calls the exit() function, or causes a fatal or system error, then the entire interpreter (including memdebug) exits.

A breakpoint can also be set using the breakpoint command; for example :-

? breakpoint /tmp/file.icn 123

Note that the full file path must be given. To clear the breakpoint, just use breakpoint with no arguments.

A break can also be set in a procedure or method; for example

? breakproc gui.Dialog.show_modal

To clear the break procedure, just use breakproc with no argument.

To stop the program at startup, run memdebug with the -m option; this sets an initial breakproc in main.

Examining memory


To examine memory, it is helpful to start with the globals command. This shows the global variable and keywords values. For example :-

? globals
&why=(0x7fd267683e6c)"No such file or directory (errno=2)"

Note that the value of &current will show us where the program was stopped. To see a "stack trace", use the list command with that co-expression :-

? list co-expression#2
    operator unary !
        Arg 0=set#25(1)
        Tended 0=D_TendPtr -> set#25(1)
        Tended 1=D_TendPtr -> &null
        Tended 2=""
        Tended 3=&null
        Tended 4=set#25(1)
    method gui.Dispatcher.do_validate
        0x246f360, caller=0x242ae70
        File dispatcher.icn; Line 148
        Temp 0=set#25(1)
        Temp 1=object FlowTermDialog#1(84)
        Temp 2=&null
        Temp 3=&null
        d=object FlowTermDialog#1(84)
        0x242ae70, caller=0x2454f60
        File dispatcher.icn; Line 58
        Temp 0=&null
    method gui.Dispatcher.work_or_sleep
        0x2454f60, caller=0x2453a90
        File dispatcher.icn; Line 108
        Temp 0=&null
    method gui.Dispatcher.message_loop
        0x2453a90, caller=0x22c07f0
        File dispatcher.icn; Line 71
        Temp 0=&null
        Temp 1=object graphics.Window#127(1)
        r=object FlowTermDialog#1(84)
        (more output...)

This shows all the values stored in internal structures and local (non-static) variables. Structures can be further examined by using list; for example :-

? list FlowTermDialog#1


To view static variables in a class, procedure or method, use the statics command, for example :-

? statics gui.Dialog
class gui.Dialog
        POINTER="left ptr"

Search for instances of a type

It is often useful to search memory for all the instances of a type. To do this, use list, and give the name of the type to search for; for example :-

? list Terminal
object Terminal#1(102)
object Terminal#3(102)
object Terminal#2(102)

The type given can be a class or record type name, or a structure name, "list", "set" or "table".

Search for references to an instance

If the list command above returns instances which you think should no longer be reachable (and hence will not be garbage collected), then the refs command can be used to see why. For example :-

? refs Terminal#3
List element list#8972(3)[3]
Instance variable parent in object gui.GridLayout#28(6)
Variable self in frame of method Terminal.initially in chain of co-expression#2432(0)
Variable self in frame of method Terminal.check_cwd_time in chain of co-expression#2432(0)
Temporary descriptor 1 in frame of method Terminal.check_cwd_time in chain of co-expression#2432(0)
Temporary descriptor 0 in frame of method Terminal.check_cwd_time in chain of co-expression#2432(0)
Object in methp block methp#884(object Terminal#3(102),method Terminal.on_filter_popup_closed)
Object in methp block methp#883(object Terminal#3(102),method Terminal.on_filter_content_changed)
Object in methp block methp#882(object Terminal#3(102),method Terminal.on_filter_action)
Object in methp block methp#989(object Terminal#3(102),method Terminal.on_trail)
Object in methp block methp#921(object Terminal#3(102),method Terminal.on_favourites_list)
Object in methp block methp#908(object Terminal#3(102),method Terminal.on_size_button)
(more output...)

refs can then be used to explore these references further; for example the first one in the list above :-

? refs list#8972
Instance variable tab_children in object gui.TabSet#1(53)


The dump command lists the values of all globals, keywords, statics and structures. Its output is usually voluminous, so it is best to redirect it to a file (or view it with less).

Values without serial numbers

Strings, csets, ucs strings, large integers and (on 32 bit systems) real numbers, lack the identifying serial numbers found in structure objects such as lists. Therefore they have to be identified by address.

For example, in the globals output above, &file was shown as :-


This address can now be used with list and refs, in the same way for structure objects, for example :-

? refs 0x7fd26bb5e252

which tells us that this string is just referenced in this one place, as the value of this keyword.

Note that some values will be allocated outside of the garbage-collected memory regions. These don't have their addresses shown, and are not subject to garbage collection, so finding references to them is not useful. For example, the value of &file in the globals output was :-


This string value is actually contained in the executable icode file loaded by the interpreter, and hence no address is shown.

Programs which load other programs

Values in loaded programs

When a program loads another program and uses classes and records in it, there can be ambiguity in the global names. For this reason, class and record type names are prefixed with the program's &main. For example,

(prog=co-expression#9(0))class Something

is a class from a loaded program whose &main is co-expression#9, whilst an instance of that class might appear as :-

(prog=co-expression#9(0))(0x7f9e73ec1480)object Something#11(3)

Note that the address of the instance is also shown, and that must be used rather than the usual class and serial combination Something#11, to search for the object with list and refs. This is because the command list Something#11 would refer to the class Something in the current program (see below) rather than in the loaded program. They will either be distinct classes (with the same name), or more likely, the current program won't even have a class Something.

References found in other programs

When a reference to an object is found in another program, it is shown with a reference to the other program's &main; for example :-

? refs Something#2
Global variable g2 of program co-expression#9(0)

Listing and selecting the current program

Memdebug has the notion of a monitored program (the one given on the command line), and the current program, which is the one whose memory is being examined. They start off as the same program. If the monitored program loads other programs, then it may be necessary to change the current program. This is because :-

To list the programs loaded by the interpreter use the progs command :-

? progs
Program 0x2209e00
Program 0x21eea10 (current)
Program 0x6bf140

Here, the monitored program is the current program, which has loaded another program (co-expression#9). co-expression#1 is the memdebug program itself. To change to the loaded program, use :-

? prog co-expression#9
prog set to 0x2209e00

Note that, although the current program can be changed, the monitored program cannot. Therefore, for example, breakpoints cannot be set in a loaded program.

Other commands

Type help at the prompt to see a list of other commands.