[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15. The GNAT Metric Tool gnatmetric

The gnatmetric tool is an ASIS-based utility for computing various program metrics. It takes an Ada source file as input and generates a file containing the metrics data as output. Various switches control which metrics are computed and output.

gnatmetric generates and uses the ASIS tree for the input source and thus requires the input to be syntactically and semantically legal. If this condition is not met, gnatmetric will generate an error message; no metric information for this file will be computed and reported.

If the compilation unit contained in the input source depends semantically upon units in files located outside the current directory, you have to provide the source search path when invoking gnatmetric. If it depends semantically upon units that are contained in files with names that do not follow the GNAT file naming rules, you have to provide the configuration file describing the corresponding naming scheme (see the description of the gnatmetric switches below.) Alternatively, you may use a project file and invoke gnatmetric through the gnat driver (see 12.2 The GNAT Driver and Project Files).

The gnatmetric command has the form

 
$ gnatmetric [switches] {filename} [-cargs gcc_switches]

where

15.1 Switches for gnatmetric  


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15.1 Switches for gnatmetric

The following subsections describe the various switches accepted by gnatmetric, organized by category.

15.1.1 Output File Control  
15.1.2 Disable Metrics For Local Units  
15.1.3 Specifying a set of metrics to compute  
15.1.4 Other gnatmetric Switches  
15.1.5 Generate project-wide metrics  


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15.1.1 Output File Control

gnatmetric has two output formats. It can generate a textual (human-readable) form, and also XML. By default only textual output is generated.

When generating the output in textual form, gnatmetric creates for each Ada source file a corresponding text file containing the computed metrics, except for the case when the set of metrics specified by gnatmetric parameters consists only of metrics that are computed for the whole set of analyzed sources, but not for each Ada source. By default, this file is placed in the same directory as where the source file is located, and its name is obtained by appending the `.metrix' suffix to the name of the input file.

All the output information generated in XML format is placed in a single file. By default this file is placed in the current directory and has the name `metrix.xml'.

Some of the computed metrics are summed over the units passed to gnatmetric; for example, the total number of lines of code. By default this information is sent to `stdout', but a file can be specified with the `-og' switch.

The following switches control the gnatmetric output:

`-x'
Generate the XML output

`-xs'
Generate the XML output and the XML schema file that describes the structure of the XML metric report, this schema is assigned to the XML file. The schema file has the same name as the XML output file with `.xml' suffix replaced with `.xsd'

`-nt'
Do not generate the output in text form (implies `-x')

`-d output_dir'
Put text files with detailed metrics into output_dir

`-o file_suffix'
Use file_suffix, instead of `.metrix' in the name of the output file.

`-og file_name'
Put global metrics into file_name

`-ox file_name'
Put the XML output into file_name (also implies `-x')

`-sfn'
Use "short" source file names in the output. (The gnatmetric output includes the name(s) of the Ada source file(s) from which the metrics are computed. By default each name includes the absolute path. The `-sfn' switch causes gnatmetric to exclude all directory information from the file names that are output.)


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15.1.2 Disable Metrics For Local Units

gnatmetric relies on the GNAT compilation model - one compilation unit per one source file. It computes line metrics for the whole source file, and it also computes syntax and complexity metrics for the file's outermost unit.

By default, gnatmetric will also compute all metrics for certain kinds of locally declared program units:

These kinds of entities will be referred to as eligible local program units, or simply eligible local units, in the discussion below.

Note that a subprogram declaration, generic instantiation, or renaming declaration only receives metrics computation when it appear as the outermost entity in a source file.

Suppression of metrics computation for eligible local units can be obtained via the following switch:

`-nolocal'
Do not compute detailed metrics for eligible local program units


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15.1.3 Specifying a set of metrics to compute

By default all the metrics are computed and reported. The switches described in this subsection allow you to control, on an individual basis, whether metrics are computed and reported. If at least one positive metric switch is specified (that is, a switch that defines that a given metric or set of metrics is to be computed), then only explicitly specified metrics are reported.

15.1.3.1 Line Metrics Control  
15.1.3.2 Syntax Metrics Control  
15.1.3.3 Complexity Metrics Control  
15.1.3.4 Coupling Metrics Control  


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15.1.3.1 Line Metrics Control

For any (legal) source file, and for each of its eligible local program units, gnatmetric computes the following metrics:

gnatmetric sums the values of the line metrics for all the files being processed and then generates the cumulative results. The tool also computes for all the files being processed the average number of code lines in bodies.

You can use the following switches to select the specific line metrics to be computed and reported.

`--lines-all'
Report all the line metrics

`--no-lines-all'
Do not report any of line metrics

`--lines'
Report the number of all lines

`--no-lines'
Do not report the number of all lines

`--lines-code'
Report the number of code lines

`--no-lines-code'
Do not report the number of code lines

`--lines-comment'
Report the number of comment lines

`--no-lines-comment'
Do not report the number of comment lines

`--lines-eol-comment'
Report the number of code lines containing end-of-line comments

`--no-lines-eol-comment'
Do not report the number of code lines containing end-of-line comments

`--lines-ratio'
Report the comment percentage in the program text

`--no-lines-ratio'
Do not report the comment percentage in the program text

`--lines-blank'
Report the number of blank lines

`--no-lines-blank'
Do not report the number of blank lines

`--lines-average'
Report the average number of code lines in subprogram bodies, task bodies, entry bodies and statement sequences in package bodies. The metric is computed and reported for the whole set of processed Ada sources only.

`--no-lines-average'
Do not report the average number of code lines in subprogram bodies, task bodies, entry bodies and statement sequences in package bodies.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15.1.3.2 Syntax Metrics Control

gnatmetric computes various syntactic metrics for the outermost unit and for each eligible local unit:

LSLOC ("Logical Source Lines Of Code")
The total number of declarations and the total number of statements. Note that the definition of declarations is the one given in the reference manual:

"Each of the following is defined to be a declaration: any basic_declaration; an enumeration_literal_specification; a discriminant_specification; a component_declaration; a loop_parameter_specification; a parameter_specification; a subprogram_body; an entry_declaration; an entry_index_specification; a choice_parameter_specification; a generic_formal_parameter_declaration."

This means for example that each enumeration literal adds one to the count, as well as each subprogram parameter.

Thus the results from this metric will be significantly greater than might be expected from a naive view of counting semicolons.

Maximal static nesting level of inner program units
According to Ada Reference Manual, 10.1(1), "A program unit is either a package, a task unit, a protected unit, a protected entry, a generic unit, or an explicitly declared subprogram other than an enumeration literal."

Maximal nesting level of composite syntactic constructs
This corresponds to the notion of the maximum nesting level in the GNAT built-in style checks (see section 3.2.5 Style Checking)

For the outermost unit in the file, gnatmetric additionally computes the following metrics:

Public subprograms
This metric is computed for package specs. It is the number of subprograms and generic subprograms declared in the visible part (including the visible part of nested packages, protected objects, and protected types).

All subprograms
This metric is computed for bodies and subunits. The metric is equal to a total number of subprogram bodies in the compilation unit. Neither generic instantiations nor renamings-as-a-body nor body stubs are counted. Any subprogram body is counted, independently of its nesting level and enclosing constructs. Generic bodies and bodies of protected subprograms are counted in the same way as "usual" subprogram bodies.

Public types
This metric is computed for package specs and generic package declarations. It is the total number of types that can be referenced from outside this compilation unit, plus the number of types from all the visible parts of all the visible generic packages. Generic formal types are not counted. Only types, not subtypes, are included.

Along with the total number of public types, the following types are counted and reported separately:

All types
This metric is computed for any compilation unit. It is equal to the total number of the declarations of different types given in the compilation unit. The private and the corresponding full type declaration are counted as one type declaration. Incomplete type declarations and generic formal types are not counted. No distinction is made among different kinds of types (abstract, private etc.); the total number of types is computed and reported.

By default, all the syntax metrics are computed and reported. You can use the following switches to select specific syntax metrics.

`--syntax-all'
Report all the syntax metrics

`--no-syntax-all'
Do not report any of syntax metrics

`--declarations'
Report the total number of declarations

`--no-declarations'
Do not report the total number of declarations

`--statements'
Report the total number of statements

`--no-statements'
Do not report the total number of statements

`--public-subprograms'
Report the number of public subprograms in a compilation unit

`--no-public-subprograms'
Do not report the number of public subprograms in a compilation unit

`--all-subprograms'
Report the number of all the subprograms in a compilation unit

`--no-all-subprograms'
Do not report the number of all the subprograms in a compilation unit

`--public-types'
Report the number of public types in a compilation unit

`--no-public-types'
Do not report the number of public types in a compilation unit

`--all-types'
Report the number of all the types in a compilation unit

`--no-all-types'
Do not report the number of all the types in a compilation unit

`--unit-nesting'
Report the maximal program unit nesting level

`--no-unit-nesting'
Do not report the maximal program unit nesting level

`--construct-nesting'
Report the maximal construct nesting level

`--no-construct-nesting'
Do not report the maximal construct nesting level


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15.1.3.3 Complexity Metrics Control

For a program unit that is an executable body (a subprogram body (including generic bodies), task body, entry body or a package body containing its own statement sequence) gnatmetric computes the following complexity metrics:

The McCabe cyclomatic complexity metric is defined in http://www.mccabe.com/pdf/mccabe-nist235r.pdf

According to McCabe, both control statements and short-circuit control forms should be taken into account when computing cyclomatic complexity. For each body, we compute three metric values:

The origin of cyclomatic complexity metric is the need to estimate the number of independent paths in the control flow graph that in turn gives the number of tests needed to satisfy paths coverage testing completeness criterion. Considered from the testing point of view, a static Ada loop (that is, the loop statement having static subtype in loop parameter specification) does not add to cyclomatic complexity. By providing `--no-static-loop' option a user may specify that such loops should not be counted when computing the cyclomatic complexity metric

The Ada essential complexity metric is a McCabe cyclomatic complexity metric counted for the code that is reduced by excluding all the pure structural Ada control statements. An compound statement is considered as a non-structural if it contains a raise or return statement as it subcomponent, or if it contains a goto statement that transfers the control outside the operator. A selective accept statement with terminate alternative is considered as non-structural statement. When computing this metric, exit statements are treated in the same way as goto statements unless `-ne' option is specified.

The Ada essential complexity metric defined here is intended to quantify the extent to which the software is unstructured. It is adapted from the McCabe essential complexity metric defined in http://www.mccabe.com/pdf/mccabe-nist235r.pdf but is modified to be more suitable for typical Ada usage. For example, short circuit forms are not penalized as unstructured in the Ada essential complexity metric.

When computing cyclomatic and essential complexity, gnatmetric skips the code in the exception handlers and in all the nested program units.

By default, all the complexity metrics are computed and reported. For more fine-grained control you can use the following switches:

`--complexity-all'
Report all the complexity metrics

`--no-complexity-all'
Do not report any of complexity metrics

`--complexity-cyclomatic'
Report the McCabe Cyclomatic Complexity

`--no-complexity-cyclomatic'
Do not report the McCabe Cyclomatic Complexity

`--complexity-essential'
Report the Essential Complexity

`--no-complexity-essential'
Do not report the Essential Complexity

`--loop-nesting'
Report maximal loop nesting level

`--no-loop-nesting'
Do not report maximal loop nesting level

`--complexity-average'
Report the average McCabe Cyclomatic Complexity for all the subprogram bodies, task bodies, entry bodies and statement sequences in package bodies. The metric is computed and reported for whole set of processed Ada sources only.

`--no-complexity-average'
Do not report the average McCabe Cyclomatic Complexity for all the subprogram bodies, task bodies, entry bodies and statement sequences in package bodies

`-ne'
Do not consider exit statements as gotos when computing Essential Complexity

`--no-static-loop'
Do not consider static loops when computing cyclomatic complexity

`--extra-exit-points'
Report the extra exit points for subprogram bodies. As an exit point, this metric counts return statements and raise statements in case when the raised exception is not handled in the same body. In case of a function this metric subtracts 1 from the number of exit points, because a function body must contain at least one return statement.

`--no-extra-exit-points'
Do not report the extra exit points for subprogram bodies


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15.1.3.4 Coupling Metrics Control

Coupling metrics measure the dependencies between a given entity and other entities the program consists of. The goal of these metrics is to estimate the stability of the whole program considered as the collection of entities (modules, classes etc.).

Gnatmetric computes the following coupling metrics:

Two kinds of coupling metrics are computed:

fan-out coupling (efferent coupling)
the number of entities the given entity depends upon. It estimates in what extent the given entity depends on the changes in "external world"

fan-in coupling (afferent coupling)
the number of entities that depend on a given entity. It estimates in what extent the "external world" depends on the changes in a given entity

Object-oriented coupling metrics are metrics that measure the dependencies between a given class (or a group of classes) and the other classes in the program. In this subsection the term "class" is used in its traditional object-oriented programming sense (an instantiable module that contains data and/or method members). A category (of classes) is a group of closely related classes that are reused and/or modified together.

A class K's fan-out coupling is the number of classes that K depends upon. A category's fan-out coupling is the number of classes outside the category that the classes inside the category depend upon.

A class K's fan-in coupling is the number of classes that depend upon K. A category's fan-in coupling is the number of classes outside the category that depend on classes belonging to the category.

Ada's implementation of the object-oriented paradigm does not use the traditional class notion, so the definition of the coupling metrics for Ada maps the class and class category notions onto Ada constructs.

For the coupling metrics, several kinds of modules -- a library package, a library generic package, and a library generic package instantiation -- that define a tagged type or an interface type are considered to be a class. A category consists of a library package (or a library generic package) that defines a tagged or an interface type, together with all its descendant (generic) packages that define tagged or interface types. That is a category is an Ada hierarchy of library-level program units. So class coupling in case of Ada is called as tagged coupling, and category coupling - as hierarchy coupling.

For any package counted as a class, its body and subunits (if any) are considered together with its spec when counting the dependencies, and coupling metrics are reported for spec units only. For dependencies between classes, the Ada semantic dependencies are considered. For object-oriented coupling metrics, only dependencies on units that are considered as classes, are considered.

For unit and control coupling also not compilation units but program units are counted. That is, for a package, its spec, its body and its subunits (if any) are considered as making up one unit, and the dependencies that are counted are the dependencies of all these compilation units collected together as the dependencies as a (whole) unit. And metrics are reported for spec compilation units only (or for a subprogram body unit in case if there is no separate spec for the given subprogram).

For unit coupling, dependencies between all kinds of program units are considered. For control coupling, for each unit the dependencies of this unit upon units that define subprograms are counted, so control fan-out coupling is reported for all units, but control fan-in coupling - only for the units that define subprograms.

The following simple example illustrates the difference between unit coupling and control coupling metrics:

 
package Lib_1 is
    function F_1 (I : Integer) return Integer;
end Lib_1;

package Lib_2 is
    type T_2 is new Integer;
end Lib_2;

package body Lib_1 is
    function F_1 (I : Integer) return Integer is
    begin
       return I + 1;
    end F_1;
end Lib_1;

with Lib_2; use Lib_2;
package Pack is
    Var : T_2;
    function Fun (I : Integer) return Integer;
end Pack;

with Lib_1; use Lib_1;
package body Pack is
    function Fun (I : Integer) return Integer is
    begin
       return F_1 (I);
    end Fun;
end Pack;

if we apply gnatmetric with --coupling-all option to these units, the result will be:

 
Coupling metrics:
=================
    Unit Lib_1 (C:\customers\662\L406-007\lib_1.ads)
       control fan-out coupling  : 0
       control fan-in coupling   : 1
       unit fan-out coupling     : 0
       unit fan-in coupling      : 1

    Unit Pack (C:\customers\662\L406-007\pack.ads)
       control fan-out coupling  : 1
       control fan-in coupling   : 0
       unit fan-out coupling     : 2
       unit fan-in coupling      : 0

    Unit Lib_2 (C:\customers\662\L406-007\lib_2.ads)
       control fan-out coupling  : 0
       unit fan-out coupling     : 0
       unit fan-in coupling      : 1

The result does not contain values for object-oriented coupling because none of the argument unit contains a tagged type and therefore none of these units can be treated as a class.

Pack (considered as a program unit, that is spec+body) depends on two units - Lib_1 and Lib_2, therefore it has unit fan-out coupling equals to 2. And nothing depend on it, so its unit fan-in coupling is 0 as well as control fan-in coupling. Only one of the units Pack depends upon defines a subprogram, so its control fan-out coupling is 1.

Lib_2 depends on nothing, so fan-out metrics for it are 0. It does not define a subprogram, so control fan-in metric cannot be applied to it, and there is one unit that depends on it (Pack), so it has unit fan-in coupling equals to 1.

Lib_1 is similar to Lib_2, but it does define a subprogram. So it has control fan-in coupling equals to 1 (because there is a unit depending on it).

When computing coupling metrics, gnatmetric counts only dependencies between units that are arguments of the gnatmetric call. Coupling metrics are program-wide (or project-wide) metrics, so to get a valid result, you should call gnatmetric for the whole set of sources that make up your program. It can be done by calling gnatmetric from the GNAT driver with `-U' option (see 12.2 The GNAT Driver and Project Files for details).

By default, all the coupling metrics are disabled. You can use the following switches to specify the coupling metrics to be computed and reported:

`--coupling-all'
Report all the coupling metrics

`--tagged-coupling-out'
Report tagged (class) fan-out coupling

`--tagged-coupling-in'
Report tagged (class) fan-in coupling

`--hierarchy-coupling-out'
Report hierarchy (category) fan-out coupling

`--hierarchy-coupling-in'
Report hierarchy (category) fan-in coupling

`--unit-coupling-out'
Report unit fan-out coupling

`--unit-coupling-in'
Report unit fan-in coupling

`--control-coupling-out'
Report control fan-out coupling

`--control-coupling-in'
Report control fan-in coupling


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15.1.4 Other gnatmetric Switches

Additional gnatmetric switches are as follows:

`-files filename'
Take the argument source files from the specified file. This file should be an ordinary text file containing file names separated by spaces or line breaks. You can use this switch more than once in the same call to gnatmetric. You also can combine this switch with an explicit list of files.

`-v'
Verbose mode; gnatmetric generates version information and then a trace of sources being processed.

`-q'
Quiet mode.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

15.1.5 Generate project-wide metrics

In order to compute metrics on all units of a given project, you can use the gnat driver along with the `-P' option:
 
   gnat metric -Pproj

If the project proj depends upon other projects, you can compute the metrics on the project closure using the `-U' option:
 
   gnat metric -Pproj -U

Finally, if not all the units are relevant to a particular main program in the project closure, you can generate metrics for the set of units needed to create a given main program (unit closure) using the `-U' option followed by the name of the main unit:
 
   gnat metric -Pproj -U main


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by GNAT Mailserver on May, 10 2012 using texi2html