Subsections

9. Debugging your Programs

Free Pascal supports debug information for the GNU debugger gdb, or it's derivatives Insight on win32 or ddd on LINUX.

This chapter describes shortly how to use this feature. It doesn't attempt to describe completely the GNU debugger, however. For more information on the workings of the GNU debugger, see the gdb users' guide.

Free Pascal also suports gprof, the GNU profiler, see section [*] for more information on profiling.

9.1 Compiling your program with debugger support

First of all, you must be sure that the compiler is compiled with debugging support. Unfortunately, there is no way to check this at run time, except by trying to compile a program with debugging support.

To compile a program with debugging support, just specify the -g option on the command-line, as follows:

ppc386 -g hello.pp
This will generate debugging information in the executable from your program. You will notice that the size of the executable increases substantially because of this9.1.

Note that the above will only generate debug information for the code that has been generated when compiling hello.pp. This means that if you used some units (the system unit, for instance) which were not compiled with debugging support, no debugging support will be available for the code in these units.

There are 2 solutions for this problem.

  1. Recompile all units manually with the -g option.
  2. Specify the 'build' option (-B) when compiling with debugging support. This will recompile all units, and insert debugging information in each of the units.
The second option may have undesirable side effects. It may be that some units aren't found, or compile incorrectly due to missing conditionals, etc..

If all went well, the executable now contains the necessary information with which you can debug it using GNU gdb.


9.2 Using gdb to debug your program

To use gdb to debug your program, you can start the debugger, and give it as an option the full name of your program:

gdb hello
Or, under DOS:
gdb hello.exe

This starts the debugger, and the debugger immediately loads your program into memory, but it does not run the program yet. Instead, you are presented with the following (more or less) message, followed by the gdb prompt '(gdb)':

GDB is free software and you are welcome to distribute copies of it
 under certain conditions; type "show copying" to see the conditions.
There is absolutely no warranty for GDB; type "show warranty" for details.
GDB 4.15.1 (i486-slackware-linux),
Copyright 1995 Free Software Foundation, Inc...
(gdb)
To start the program you can use the run command. You can optionally specify command-line parameters, which will then be fed to your program, for example:
(gdb) run -option -anotheroption needed_argument
If your program runs without problems, gdb will inform you of this, and return the exit code of your program. If the exit code was zero, then the message 'Program exited normally'.

If something went wrong (a segmentation fault or so), gdb will stop the execution of your program, and inform you of this with an appropriate message. You can then use the other gdb commands to see what happened. Alternatively, you can instruct gdb to stop at a certain point in your program, with the break command.

Here is a short list of gdb commands, which you are likely to need when debugging your program:

quit
Exits the debugger.
kill
Stops a running program.
help
Gives help on all gdb commands.
file
Loads a new program into the debugger.
directory
Add a new directory to the search path for source files.

Remark: My copy of gdb needs '.' to be added explicitly to the search path, otherwise it doesn't find the sources.

list
Lists the program sources per 10 lines. As an option you can specify a line number or function name.
break
Sets a breakpoint at a specified line or function
awatch
Sets a watch-point for an expression. A watch-point stops execution of your program whenever the value of an expression is either read or written.

for more information, see the gdb users' guide, or use the 'help' function in gdb.

The appendix [*] contains a sample init file for gdb, which produces good results when debugging Free Pascal programs.

It is also possible to use RHIDE, a text-based IDE that uses gdb. There is a version of RHIDE available that can work together with FPC.

9.3 Caveats when debugging with gdb

There are some peculiarities of Free Pascal which you should be aware of when using gdb. We list the main ones here:

  1. Free Pascal generates information for GDB in uppercare letters. This is a consequence of the fact that pascal is a case insensitive language. So, when referring to a variable or function, you need to make it's name all uppercase.

    As an example, of you want to watch the value of a loop variable count, you should type

    watch COUNT
    
    Or if you want stop when a certain function (e.g MyFunction) is called, type
    break MYFUNCTION
    

  2. Line numbers may be off by a little. This is a bug in Free Pascal and will be fixed as soon as possible.

  3. gdb does not know sets.

  4. gdb doesn't know strings. Strings are represented in gdb as records with a length field and an array of char contaning the string.

    You can also use the following user function to print strings:

    define pst
    set $pos=&$arg0
    set $strlen = {byte}$pos
    print {char}&$arg0.st@($strlen+1)
    end
    
    document pst
      Print out a Pascal string
    end
    
    If you insert it in your gdb.ini file, you can look at a string with this function. There is a sample gdb.ini in appendix [*].

  5. Objects are difficult to handle, mainly because gdb is oriented towards C and C++. The workaround implemented in Free Pascal is that object methods are represented as functions, with an extra parameter this (all lowercase !) The name of this function is a concatenation of the object type and the function name, separated by two underscore characters.

    For example, the method TPoint.Draw would be converted to TPOINT__DRAW, and could be stopped at with

    break TPOINT__DRAW
    

  6. Global overloaded functions confuse gdb because they have the same name. Thus you cannot set a breakpoint at an overloaded function, unless you know it's line number, in which case you can set a breakpoint at the starting linenumber of the function.


9.4 Support for gprof, the GNU profiler

You can compile your programs with profiling support. for this, you just have to use the compiler switch -pg. The compiler wil insert the necessary stuff for profiling.

When you have done this, you can run your program as you normally would run it.

yourexe
Where yourexe is the name of your executable.

When your program finishes a file called gmon.out is generated. Then you can start the profiler to see the output. You can better redirect the output to a file, becuase it could be quite a lot:

gprof yourexe > profile.log

Hint: you can use the -flat option to reduce the amount of output of gprof. It will then only output the information about the timings

For more information on the GNU profiler gprof, see its manual.


9.5 Detecting heap memory leaks

Free Pascal has a built in mechanism to detect memory leaks. There is a plug-in unit for the memory manager that analyses the memory allocation/deallocation and which prints a memory usage report after the program exits.

The unit that does this is called heaptrc. If you want to use it, you should include it as the first unit in you uses clause. Alternatively, you can supply the -gh switch to the compiler, and it will include the unit automatically for you.

After the program exits, you will get a report looking like this:

Marked memory at 0040FA50 invalid
Wrong size : 128 allocated 64 freed
  0x00408708
  0x0040CB49
  0x0040C481
Call trace for block 0x0040FA50 size 128
  0x0040CB3D
  0x0040C481
The output of the heaptrc unit is customizable by setting some variables.

You can find more information about the usage of the heaptrc unit in the Unit reference.


9.6 Line numbers in run-time error backtraces

Normally, when a run-time error occurs, you are presented with a list of addresses that represent the call stack backtrace, i.e. the addresses of all procedures that were invoked when the run-time error occurred.

This list is not very informative, so there exists a unit that generates the file names and line numbers of the called procedures using the addresses of the stack backtrace. This unit is called lineinfo.

You can use this unit by giving the -gl option to the compiler. The unit will be automatically included. It is also possible to use the unit explicitly in your uses clause, but you must make sure that you compile your program with debug info.

Here is an example program:

program testline;

procedure generateerror255;

begin
  runerror(255);
end;

procedure generateanerror;

begin
  generateerror255;
end;

begin
  generateanerror;
end.
When compiled with -gl, the following output is generated:
Runtime error 255 at 0x0040BDE5
  0x0040BDE5  GENERATEERROR255,  line 6 of testline.pp
  0x0040BDF0  GENERATEANERROR,  line 13 of testline.pp
  0x0040BE0C  main,  line 17 of testline.pp
  0x0040B7B1
Which is more understandable than the normal message. Make sure that all units you use are compiled with debug info, because if they are not, no line number and filename can be found.

9.7 Combining heaptrc and lineinfo

If you combine the lineinfo and the heaptrc information, then the output of the heaptrc unit will contain the names of the files and line numbers of the procedures that occur in the stack backtrace.

In such a case, the output will look something like this:

Marked memory at 00410DA0 invalid
Wrong size : 128 allocated 64 freed
  0x004094B8
  0x0040D8F9  main,  line 25 of heapex.pp
  0x0040D231
Call trace for block 0x00410DA0 size 128
  0x0040D8ED  main,  line 23 of heapex.pp
  0x0040D231
If lines without filename/line-number occur, this means there is a unit which has no debug info included. (in the above case, the getmem call itself)



root
2000-12-20