8 Debugging Your Project

 

  <--Last Chapter Table of Contents Next Chapter-->  

 

8.1 Limit and the Heap Size

The default storage pool (or the "heap") is kept on the user's stack. Unusually large variables in subprograms (including the main program) can cause out of memory errors. Variables in packages are not affected by the stack size. You can increase the stack space using Linux's limit command (although using dynamic allocation is usually a better solution).
 limit stacksize 1024 kbytes # 1 Megabyte user stack

To constrain the size of your stack, as far as Gnat is concerned, use the GNAT_STACK_LIMIT environment variable to indicate the number of kilobytes of stack space. In individual Ada tasks, the stack size can be set by pragma Storage_Size.

Stack size checking is normally disabled by Gnat. Section 4.4 discusses this.

 

8.2 The Debugging Pragmas

 
Ada Feature
Description
C Equivalent
pragma Assert( condition);
Assert a condition.
assert
pragma Debug( Procedure );
Debugging procedure call
 -
pragma Suppress_Debug_Info;
Disable pragma debug
#ifdef var...#endif
pragma No_Return( subprogram );
Indicate a subprogram that never completes
 -
pragma Initialize_Scalars;
Initialize scalars to illegal values
 -
GNAT provides two useful pragma for debugging programs. To use these pragmas, you need to use the -gnata option during compiling. Removing -gnata causes GNAT to ignore these pragmas, making it easy to compile a version of your program for public release without having to delete the debugging statements from your source code.Pragma Assert lets you test to make sure a certain condition is true. If it isn't, then an exception is raised. Use Assert to check for conditions which you assume are true during program development. This is especially useful when several programmers are working on a project and you don't know if a condition will change in the future.
  pragma Assert( ScreenHeight = 24 );

In this example, if the variable ScreenSize is not 24, an ASSERT_ERROR exception is raised.

Pragma Debug lets you call a procedure for debugging purposes. For example, you can use this to print information about the program while it is running. Because this is a pragma, you can place it almost anywhere, even in the middle of variable declarations.

  x := 5;
  pragma Debug( PrintToLogFile( "X is now" & x'img ) );

If PrintToLogFile is a procedure that saves messages to a log file, this example saves the message "X is now 5" to the log file.

Pragma debug can be disabled with pragma Suppress_Debug_Info.

Pragma No_Return can be used to indicate subprograms that never complete. This suppresses the related compiler warnings.

Pragma Initialize_Scalars initializes anything not an array, record or tagged record to illegal values wherever possible. This pragma helps expose variables used before they are initialized. Use this at the start of a program or package.

In more recent versions of Gnat, a new pragma Initialize_Scalars should be used due to some confusion about what the older Normalize_Scalars affects. Initialize_Scalars is very similar to Normalize_Scalars but is easier to use (no need to have the pragma used for the full partition). This e.g. means you do not have to recompile the run-time. Also, the initial values to initialize the scalars can be decided at bind time. With Normalize_Scalars, there is no control over the values to initialize the scalars.

Suppose you have a integer variable with a range between 1 and 100. Normally, Ada won't assign an initial value (unless you specify one). With Initialize_Scalars, your variable will be initialized to some value out of range, perhaps -1. If you attempt to use this variable, you'll probably raise a CONSTRAINT_ERROR exception.

Initialize_Scalars works better if you use the -gnatVf switch.
 

8.3 Identifying Files

[Rewrite and Expand]From Usenet:

> If you are using 2.2.x, you can use the /proc to find a process which is> using a directory or file. If is the case, try this: ls -lad `find> /proc` | grep home> The number after the /proc should be the process ID.>> Any way, it seems a little bit delicate umount your /home after the> boot... Couldn't you use a rescue disk and format your /home without> mount it in a boot?>> Are you sure your /home isn't only a part of your / (root directory)?> When you type "df", does it report a different device for the /home?> Sorry, if I'm asking a very basic question.>try fuser or lsof.
 

8.4 Compiler Info with -gnatG

The -gnatG compiler switch shows Gnat's interpretation of your source code after its initial analysis. If you specify -gnatD, Gnat will write this information to a file ending in .dg (for "debug").

The following is a listing of the "pointers" program used later in this book:

with Ada.Text_IO, System.Address_To_Access_Conversions;
use Ada.Text_IO;

procedure pointers is

  package IntPtrs is new System.Address_To_Access_Conversions( integer );
  -- Instantiate a package to convert access types to/from addresses.
  -- This creates an integer access type called Object_Pointer.

  five        : aliased integer := 5;
  -- Five is aliased because we will be using access types on it

  int_pointer : IntPtrs.Object_Pointer;
  -- This is an Ada access all type

  int_address : System.Address;
  -- This is an address in memory, a C pointer

begin

  int_pointer := five'unchecked_access;
  -- Unchecked_access needed because five is local to main program.
  -- If it was global, we could use 'access.

  int_address := five'address;
  -- Addresses can be found with the 'address attribute.
  -- This is the equivalent of a C pointer.

  int_pointer := IntPtrs.To_Pointer( int_address );
  int_address := IntPtrs.To_Address( int_pointer );
  -- Convert between Ada and C pointer types.

end pointers;

The -gnatG shows the compiler's analysis of your program. In this case, it displays the results of the instantiation of the generic package:

with system;
with ada;
with ada.text_io;
with system.address_to_access_conversions;
use ada.text_io;
with system;
with system;
with unchecked_conversion;

procedure pointers is
   
   package intptrs is
      subtype object is integer;
      package address_to_access_conversions renames intptrs;
      null;
      type object_pointer is access all object;
      for object_pointer'size use 32;
      function to_pointer (value : address) return object_pointer;
      function to_address (value : object_pointer) return address;
      pragma convention (intrinsic, to_pointer);
      pragma convention (intrinsic, to_address);
      freeze object_pointer []
      freeze to_pointer []
      freeze to_address []
   end intptrs;
   
   package body intptrs is
      
      function to_address (value : object_pointer) return address is
      begin
         if value = null then
            return null_address;
         else
            return value.all'address;
         end if;
      end to_address;
      
      function to_pointer (value : address) return object_pointer is
         
         package a_to_pGP3183 is
            subtype source is address;
            subtype target is object_pointer;
            function a_to_pR (s : source) return target;
         end a_to_pGP3183;
         function a_to_p is new unchecked_conversion (address, 
           object_pointer);
      begin
         return target!(source(value));
      end to_pointer;
   end intptrs;
   
   package intptrs is new system.address_to_access_conversions (integer);
   five : aliased integer := 5;
   int_pointer : intptrs.object_pointer := null;
   int_address : system.address;
   freeze intptrs []
begin
   int_pointer := five'unchecked_access;
   int_address := five'address;
   int_pointer := intptrs.to_pointer (int_address);
   int_address := intptrs.to_address (int_pointer);
   return;
end pointers;

8.5 Floating Point Numbers

The GCC FAQ reports that floating point rounding problems can occur with -O2 and -O3 unless you use -ffloat-store (keeps floating-point numbers out of CPU registers). Using this switch will slow your program.

8.6 Gdb: the GNU debugger

gnat 3.11 and later with a version of Gdb, the GNU command line debugger, that fully supports Ada data structures.You shouldn't have to use Gdb very often as most problems are solvable with a few well-placed put_line's. However, if the program produces are core file or is behaving unpredictably because of an obscure coding mistake or a compiler bug, Gdb is the best way to find out what is going wrong and where.

In order to use Gdb, you must compile your program with debugging support enabled (with the -g option in gnat, or -ggdb in C).

To start Gdb on a program called dbase, use

gdb dbase

(or gnatgdb with the ALT version) and Gdb responds with its command line prompt, "(gdb)". Type run to start the program normally to the point of the crash. You can print out the value of variables using the print command:

(gdb) print ch

This will print the character in the variable named ch at the time when the program was stopped.

To look at a core file produced by a segmentation fault, use

gdb dbase core

You can examine the variables at the time the program crashed.

Gdb contains many other commands. You can get online help at any time with the help command.

GNAT has a hidden -gnatdg flag. If you compile your program using this flag, you'll get extra information for Gdb, such as making all temporary variables used by gnat visible to the debugger.
 

8.7 Code Restrictions

There are several pragmas for disabling certain language features. These restriction pragmas can be used to enforce a certain policy and warn a programmer when the policy is violated. For example, if you are writing a real-time program, you may want to disable Ada features that do not have a known response time so that your program will not have random delays.

The restriction pragmas include:

no_run_time also enforces restrictions because the Ada run-time library is not available.

More information on the usage of these pragmas is available in the Gnat documentation.

 

  <--Last Chapter Table of Contents Next Chapter-->