6 Development Utilities

 

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

 

6.1 Saving Time with Gnatstub

Starting with gnat 3.11p, gnat provides a prototyping tool called Gnatstub.Gnatstub takes an Ada package specification and creates a corresponding body, ready to have the details outlined in the spec filled in. These empty subprograms are sometimes called "stubs".
  This is especially useful on a large project where programmers write a series of package specs to test their design. Once the package design is set, Gnatstub can create a basic body and save the programmers the work of copying and modifying the specification by hand.
 
IDE: TIA will run gnatstub on the current file using Stub in the File menu.

For example, suppose you have the following package specification in a file called tiny.ads:

package tiny is

  procedure simple_procedure;
  function simple_function return boolean;

end tiny;

You can create a stub body for this package using

  gnatstub tiny.ads

Gnatstub produces the following tiny.adb file:

package body tiny is

---------------------
-- simple_function --
---------------------

function simple_function return boolean is
begin
  return simple_function;
end simple_function;

----------------------
-- simple_procedure --
----------------------

procedure simple_procedure is
begin
  null;
end simple_procedure;

end tiny;

This package body is in proper Ada format, ready to be compiled. Of course, it doesn't actually do anything useful. It's up to the programmer fill in the implementation details.
 

6.2 Cross-referencing with Gnatxref

Gnatxref (or gnatf in gnat 3.10) is a utility that produces an index of every occurrence of an identifier in a program, including all identifiers used by packages that the program depends upon.

The -v option produces a listing in the format for a vi editor tags file.

IDE: TIA will run gnatxref on the current file by chosing Xref in the File menu.

For our hello.adb program, Gnatxref produces the following:

  Text_IO U a-textio.ads:51:13 {} {hello.adb:1:10 4:7 }
  Put_Line U a-textio.ads:260:14 {} {hello.adb:4:15 }
  Ada U ada.ads:18:9 {} {hello.adb:1:6 4:3 }
  hello U hello.adb:2:11 {} {}

Each line begins with the identifier being indexed. The "U" means [not sure-KB]. The next segment is the file that defines the identifier, and the position in the file. The {} means [not sure-KB]. The final bracketed section lists all occurrences of the identifier in the program.

In this example, identifier text_io appears in the first line (the with) and the fourth line (the put_line).

6.3 Eliminating Dead Code with Gnatelim

gnatelim is a utility that searches for unused parts of your program in the object files and removes them from the final executable. It works by creating a list of subprograms that the compiler shouldn't compile. If you save this list as gnat.adc, gnatmake will automatically read this file and will skip these subprograms when compiling.

To use gnatelim, you need to generate tree files using the -gnatt switch. The Gnat manual recommends these steps when using gnatelim (assuming that your main program is main.adb):

  1. gnatmake -c main
  2. gnatbind main
  3. gnatmake -f -c -gnatc -gnatt main
  4. gnatelim main > gnat.adc
  5. gnatmake -f main

These commands will generate a complete set of tree files for your project, strip out all unused subprograms, and will then recompile the project as a finished executable.

gnatelim is is based on ASIS.

[gnatelim doesn't work under gnat 3.11.--KB]

6.4 Execution Stack & Memory Leak Detection

Gnat 3.11 does not display the execution stack in the event of of an exception. Gnat 3.12 provides additional information about the source of an exception. You can get additional information about the execution stack using the gnat.traceback package (12.15).

The gnatmem utility monitors a running programming using the Gnat gdb debugger.  When the program is finished running, gnatmem displays a summary of dynamically allocated memory.  You can use this information to find "memory leaks", places in your program where allocated memory was not deallocated.  Because gnatmem uses gdb, the program should be compiled with gdb support turned on (the -g switch).
 

Note: gnatmem doesn't work with Gnat 3.11.

To run gnatmem, type

  gnatmem program

The gnatmem switches are:

-q - quiet - hides statistics and shows only potential memory leaks
n - a number between 1 and 10 indicating the depth of the backtrace information
-o file - save the gdb output to the indicated file.  The gdb script is saved as gnatmem.tmp
-i file - processing using the file previously saved with -o.  Use this to test a program that crashed while gnatmem was running.
 

6.5 Conditional Compiling with Gnatprep

Although the Ada 95 design team decided against including preprocessing compiler directives like C does, gnat provides a preprocessor so you can use conditional compiling directives in your Ada programs.

Gnatprep, the Gnat PREProcessor, takes a source file with conditional directives, a file with variable assignments for the conditional directives, and produces a source file with all statements not satisfying the conditional directives removed.
 

C: The conditional directives do not allow expressions. There must only be a variable, and that variable must be true or false.
IDE: No IDE's currently support gnatprep.

Suppose you create a file called "prepvalues" with the following Gnatprep definitions:

  ALPHAVERSION := true
  BETAVERSION := false
  RELEASEVERSION := false
  TRANSLATION := English

Suppose also that you had a short program with Gnatprep statements in it:

with text_io;
use text_io;

procedure preptest is

  -- only include the relevant parts for this version
  #if ALPHAVERSION
  s_version : string := "alpha";
  #elsif BETAVERSION
  s_version : string := "beta";
  #elsif RELEASEVERSION
  s_version : string := "release";
  #else
  s_version : string := "unknown";
  #end if;

  -- string is the value of the gnatprep variable named translation
  s_translation : string := "$TRANSLATION";

begin
  Put_Line( "This is the " & s_version & " edition" );
  Put_Line( "This is the " & s_translation & " translation" );
end preptest;

Running gnatprep on the above program with the prepvalues file gives you the following program:

with text_io;
use text_io;

procedure preptest is

  -- only include the relevant parts for this version

  s_version : string := "beta";
  -- string is the value of the gnatprep variable named translation
  s_translation : string := "English";

begin

  Put_Line( "This is the " & s_version & " edition" );
  Put_Line( "This is the " & s_translation & " translation" );

end preptest;

The Gnatprep command switches are:

-Dsymbol=value - define values on the command line instead of a prep values file, same as -D in C. For example, -DMacintosh=FALSE
-b - replace gnatprep commands with blank lines (instead of -c)
-c - comment out gnatprep commands (instead of -b)
-r - generate a Source_Reference pragma
-s - print a sorted list of symbols and values
-u - treat undefined symbols as if they were FALSE

C: There is no gnatprep equivalent of __FILE__ (name of current source file) or __LINE__ (number of current line).

6.6 Profiling with Gprof

Gprof in the GNU profiler. It shows which subprograms in a program are being executed the most. You can use this information to find the parts of a program with the greatest need for CPU efficiency and hand optimize those parts accordingly.

To use Gprof, you must rebuild your project using the -pg switch at both the compiling and linking stages. With Gnatmake, you must include -pg with both -cargs and -largs switches.
 

IDE: TIA will profile your project with gprof if you select Profile in the Project menu.  It automatically rebuilds your project with the necessary gprof switches, starts your main program, and then displays the gprof results.

For example, we can use Gprof on the following factorial program:

package fact is
  function factorial( param : integer ) return integer;
end fact;

package body fact is

  function factorial( param : integer ) return integer is
  begin
    if param < 2 then
       return 1;
    end if;
    return param * factorial( param - 1 );
  end factorial;

end fact;

with fact;
use fact;
procedure bench2 is

  maxFactorials : constant integer := 1000;

  type factorialArray is
  array( 1..maxFactorials ) of integer;

  list : factorialArray;

begin

  for i in 1..maxFactorials loop
    list( i ) := factorial( i );
  end loop;

end bench2;

After compiling and linking the program with the -pg switch, run the program. The program produces a gmon.out file containing profile information about the program. Now we can use Gprof to get an analysis of the program.

Running gprof -c bench2 returns the following information. Note that Ada subprograms are labeled with the package name, a double underscore, and the subprogram name.

[Need to clean this up--KB]
Flat profile:

Each sample counts as 0.01 seconds.

%cumulative self self total


time seconds seconds callsus/callus/callname


62.500.050.05__mcount_internal

25.000.070.02mcount


12.500.080.01100010.0010.00fact__factorial

0.000.080.0010.00
10000.00_ada_bench2


0.000.080.0010.000.00fact___elabb



%the percentage of the total running time of the
time program used by this function.

cumulative a running sum of the number of seconds accounted
seconds for by this function and those listed above it.

self the number of seconds accounted for by this

seconds function alone.This is the major sort for this listing.

calls the number of times this function was invoked, if
this function is profiled, else blank.

self the average number of milliseconds spent in this
ms/call function per call, if this function is profiled,
else blank.

total the average number of milliseconds spent in this
ms/callfunction and its descendents per call, if this 
function is profiled, else blank.

name the name of the function. This is the minor sort
for this listing. The index shows the location of
the function in the gprof listing. If the index is
in parenthesis it shows where it would appear in
the gprof listing if it were to be printed.

Call graph (explanation follows)

granularity: each sample hit covers 4 byte(s) for 100.00% of 0.01 seconds

index % time self children called name

499500fact__factorial [1]

0.010.001000/1000_ada_bench2
[3]

[1]100.00.010.001000+499500fact__factorial
[1]

0.000.000/0mcount (177)

499500fact__factorial [1]


-----------------------------------------------

0.000.000/0_start [473]

[2]100.00.000.01main [2]

0.000.011/1_ada_bench2 [3]

0.000.000/0__gnat_initialize
[399]

0.000.000/0adainit [61]

0.000.000/0__gnat_break_start
[395]

0.000.000/0adafinal [60]

0.000.000/0__gnat_finalize
[397]

0.000.000/0exit [95]


-----------------------------------------------

0.000.011/1main [2]

[3]100.00.000.011_ada_bench2
[3]

0.010.00 1000/1000fact__factorial
[1]

0.000.000/0mcount (177)


-----------------------------------------------

0.000.001/1adainit [61]

[6]0.00.000.001fact___elabb
[6]

0.000.000/0mcount (177)


-----------------------------------------------



This table describes the call tree of the program, and was sorted by
the total amount of time spent in each function and its children.

Each entry in this table consists of several lines.The line with the
index number at the left hand margin lists the current function.

The lines above it list the functions that called this function,
and the lines below it list the functions this one called.

This line lists:

index A unique number given to each element of the table.
Index numbers are sorted numerically.
The index number is printed next to every function name so
it is easier to look up where the function in the table.

% time This is the percentage of the `total' time that was spent
in this function and its children.Note that due to
different viewpoints, functions excluded by options, etc,
these numbers will NOT add up to 100%.

self This is the total amount of time spent in this function.

children This is the total amount of time propagated into this
function by its children.

called This is the number of times the function was called.
If the function called itself recursively, the number
only includes non-recursive calls, and is followed by
a `+' and the number of recursive calls.

name The name of the current function. The index number is
printed after it. If the function is a member of a
cycle, the cycle number is printed between the
function's name and the index number.

For the function's parents, the fields have the following meanings:

self This is the amount of time that was propagated directly
from the function into this parent.

children This is the amount of time that was propagated from
the function's children into this parent.

called This is the number of times this parent called the
function `/' the total number of times the function
was called. Recursive calls to the function are not
included in the number after the `/'.

name This is the name of the parent.The parent's index
number is printed after it.If the parent is a
member of a cycle, the cycle number is printed between
the name and the index number.

If the parents of the function cannot be determined, the word
`<spontaneous>' is printed in the `name' field, and all the other
fields are blank.

For the function's children, the fields have the following meanings:

self This is the amount of time that was propagated directly
from the child into the function.

children This is the amount of time that was propagated from the
child's children to the function.

called This is the number of times the function called
this child `/' the total number of times the child
was called. Recursive calls by the child are not
listed in the number after the `/'.

name This is the name of the child.The child's index
number is printed after it.If the child is a
member of a cycle, the cycle number is printed
between the name and the index number.

If there are any cycles (circles) in the call graph, there is an
entry for the cycle-as-a-whole. This entry shows who called the
cycle (as parents) and the members of the cycle (as children.)

The `+' recursive calls entry shows the number of function calls that
were internal to the cycle, and the calls entry for each member shows,
for that member, how many times it was called from other members of
the cycle.

Index by function name

(418) __mcount_internal[1]
fact__factorial[5] <cycle 2>

[3] _ada_bench2(177) mcount

[6] fact___elabb[4] <cycle
1>

If factorial is a function internal to program bench2, the function name won't show up in Gprof. For example:

procedure bench2 is

  function factorial( param : integer ) return integer is
  begin
    if param > 1 then
      return param * factorial( param - 1 );
    end if;
    return 1;
  end factorial;

  maxFactorials : constant integer := 100;
  type factorialArray is
    array( 1..maxFactorials ) of integer;

  list : factorialArray;

begin

  for i in 1..maxFactorials loop
    list( i ) := factorial( i );
  end loop;

end bench2;

gprof -c bench2 returns the following information:

Flat profile:

Each sample counts as 0.01 seconds.
no time accumulated

%cumulativeselfselftotal


time seconds seconds callsTs/callTs/callname

0.000.000.00990.000.00main


0.000.000.0010.000.00_ada_bench2

etc.


 

Note: gnatmem doesn't work with Gnat 3.11.

6.7 Static Libraries and Shared Libraries (GnatDLL)

To create static libraries (.a suffix under Linux), create the archive file by combining all the .o object files into an .a archive file using ar. Then, put the .ads and .ali files in a appropriate directories. Change the .ali files to read-only. Gnat will then search for the object files in a library. When building your project, use the appropriate switches (-I, -L, -l, etc.) so that gcc and find your library and the include files.

To create shared libraries, Gnat has a tool called gnatdll to automate the process in a operating system independent way.

[To be completed]

6.8 Source as Web Pages Using GnatHTML

This utility converts an Ada source file into a series of indexed, coloured HTML web pages.  By default, the web pages are stored under a subdirectory called HTML.  By posting your source code on a network or the Internet, developers can examine your work with a web browser.

The gnathtml switches are:

Example using gnathtml with the following program:

with Ada.Text_IO;
use Ada.Text_IO;

procedure htmltest is
  -- a demonstration of gnathtml

  function factorial( n : natural ) return natural is
  -- compute the factorial of n
  begin
    if n < 2 then
       return 1;
    else
       return n * factorial( n-1 );
    end if;
  end factorial;

-- main program

begin
  put_line( "Browse this source!" );
  new_line;
  put_line( "The factorial of 5 is" & natural'image( factorial( 5 ) ) );
end htmltest;
 


This creates an index file like this:

Files

Functions/Procedures


The first link would show you your entire file:

File : htmltest.adb

with Ada.Text_IO;
use Ada.Text_IO;

procedure htmltest is
  -- a demonstration of gnathtml

  function factorial( n : natural ) return natural is
  -- compute the factorial of n
  begin
    if n &lt; 2 then
       return 1;
    else
       return n * factorial( n-1 );
    end if;
  end factorial;

-- main program

begin
  put_line( "Browse this source!" );
  new_line;
  put_line( "The factorial of 5 is" & natural'image( factorial( 5 ) ) );
end htmltest;

The links use the .htm (not .html) extension for portability.

6.9 GnatFind

[To be written--KB]  

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