1 COMMAND FORMAT -- MCCABE : Calculate the McCabe complexity measures. -- Version 3.01-1.1 subtype FILE_NAME is STRING; subtype MAX_VALUE is INTEGER range 1 .. 99; procedure MCCABE( SOURCE_FILE : in FILE_NAME; OUTPUT_FILE : in FILE_NAME := ""; MAX_COMPLEXITY : in MAX_VALUE := 20 ); -- SOURCE_FILE : Input file name. -- OUTPUT_FILE : Name of the report file (defaults to standard output). -- MAX_COMPLEXITY : Greatest complexity not flagged in output listing -(range: 1 .. 99, default: 20). 2 MCCABE DESCRIPTION AND USE The McCabe measure computes complexity based on the control structure (or "flow graph") of the program. The tool takes the name of an Ada source program text file and computes the cyclomatic complexity for every subprogram body contained in the file. Specifications do not have a complexity associated with them. The tool also takes an output file name and a number, MAXCOMPLEXITY, which is the maximum complexity allowed for a program. If a procedure is found to have a complexity greater than the specified value, it will be flagged on the output listing. The McCabe tool builds the flow graph and computes the cyclomatic number for each program unit in the source file. In addition, it sums the number of nodes and edges in all program units and computes a cyclomatic number for the entire file. The tool assumes that a source program text is correct ADA (i.e., it is compiled cleanly); otherwise it terminates with a syntax error. 3 MOTIVATION The McCabe complexity measure is principally useful in two ways: as an aid in testing and as a numeric guidline for management purposes. In testing, the complexity number corresponds to the number of control paths through a subprogram and therefore the Page 2 number of test cases required to test them all. If the number of test cases is less than the complexity, then at least one of three conditions is true: 1. more testing is required; 2. decision points can be removed; 3. in-line code can replace decision points. (See [McC] for a full explanation.) As a programming and management guidline the tool provides a simple way of flagging subprograms which might cause problems. The general method for keeping subprograms simple is to establish a size limit, e.g., 50 lines or 2 pages. This is really a surrogate for the lack af any good way of measuring simplicity. The McCabe metric is just such a simplicity measure. The measure is only a guideline since a large CASE statement will have a correspondingly large complexity but it may only be the equivalent of a jump table. Each user will have to determine through experience how best to use the information provided by the tool. The McCabe complexity number is just one measure of program complexity. Although it can help to identify program units that may require further scrutiny, it is not at all reasonable to require that all program units have a cyclomatic complexity below a certain value. A case statement for example is well structured, but can have a high complexity (on the order of the number of branches in the statement). This does not mean that the program is poorly structured, only that each branch of the case statement must be tested. 4 THEORY OF THE MCCABE METRIC The McCabe metric is based on the flow graph of a program. The flow graph is a directed graph where the nodes represent basic blocks and the edges represent control flow between basic blocks. A basic block is a sequence of consecutive statements which may only be entered at the beginning and exited at the end. When entered the instructions are executed in sequence without branching except at the end [Aho]. Figure 1 shows some flow graphs for different programming structures. if expression then S1 else S2 endif; do while expression loop S1; end loop; Page 3 if / \ V V S1 S2 \ / V V endif do while | | ^ | V | | S1 V end loop FIGURE 1 McCabe's cyclomatic complexity is defined as: V(G) = e - n + 2p where 'e' is the number of edges in the flow graph, 'n' is the number of nodes, and 'p' is the number of connected components. Ignoring the direction from the edges of the flow graph, if there exists a path between each pair of vertices I and J then we say the graph is a connected component. Since there is a path from the entry node to every other node in the flow graph then (ignoring the directions of the edges) we will be able to get from any node I to any other node J by going through the entry node. Therefore, in our case of computing the cyclomatic complexity for an individual subprogram p is always 1. The number of connected components becomes interesting when we would like to calcultate the cyclomatic complexity of a program that has a number of different subprograms. McCabe defines the cyclomatic complexity of a system as the sum of the cyclomatic complexities of all the modules. In this case p, the number of connected components would be the number of different modules. If a subprogram has some unreachable code then McCabe's cyclomatic complexity formular does not make a lot of sense. In some cases the tool discovers such subprograms and flags them in the output with the message "unreachable code". 5 INTERPRETATION OF ADA STATEMENTS There is a number of ADA statements which generate flow but building a flow graph for them is not obvious. Sometimes it happens because the flow of a statement can be interpreted in a number of ways (as for case and select statements); sometimes it happens because of the tool limits (as for a raise statement the flow could go to an exception handler belonging to a same subprogram as the raise statement or it could go to a different subprogram in which case the tool does not have enough information to track the flow). This is a list of such statements with interpretations. their corresponding Page 4 6 1. The complexity of an exception handler is added complexity of a containing subprogram. to the 2. Case, select statements and exception handlers are interpreted as if statement [McC]. For a case statement a corresponding if statement always has an else clause. Two forms of select statements - conditional entry call and timed entry call are always interpreted as if...then...else...end if. 3. Return, raise and terminate statements are ignored (as a stop statement in [McC]). OUTPUT FORMAT The output of the tool is a list of names of all the subprograms in the input file. Each name is preceeded by its cyclomatic complexity, the number of nodes and the number of edges in the flow graph. If any subprogram or function has a cyclomatic complexity above the specified value, it is flagged. A sample output follows: MCCABE CYCLOMATIC COMPLEXITY: SOURCE_FILE "EXAMPLE" - WITH MAX_COMPLEXITY 9 COMPLEXITY CYCLOMATIC NUMBER OF EDGES NODES 4 3 14 . . . 10 9 37 . . . 8 8 25 . . . 32 90 48 SUBPROGRAM NAME subprog1 subprog2 *** subprog3 . . . Total for 5 program units Page 5 7 REFERENCES 1. [Aho] Aho, A.V., Ullman, J.D., Principles of Compiler Design, Addision-wesley Publishing Company, pp 412-413, 1977. 2. [McC] McCabe, T. J., "A Complexity Measure", IEEE Transactions on Software Engineering, Vol. SE-2, pp 308-320, December, 1976. Page 6 8 APPENDIX A: INSTALLATION AND MODIFICATION To build the McCabe tool: 1. Compile all the abstractions into a program library (see READ.ME in abstractions directory for details). 2. Compile everything named in the MCCABE.CO file into the program library containing the abstractions or a sublibrary whose parent library contains all the abstractions. MCCABE.CO lists file names in the correct compilation order. 3. Link McCabe with the program library where everything was compiled. To do this using the DEC Ada compiler type: $ acs link mccabe To run the tool on VMS: 1. Define logical symbol for the executables of the McCabe tool. For example, MCCABE :== $DRB1:[NOSC.TOOLS.MCCABE]MCCABE.EXE NOTE: The full path name of the executable is required in the definition of the symbol. The pathname given here is just an example and will be different on your system. 2. Enter a command with appropriate parameters. For example, MCCABE ("myfile"); Entering the command with no parameters gives a brief description of how to use the tool. Files contained in the MCCABE directory: MCCABE.CO MCCABE.EXE MCCABE.CNT READ.ME ----- Compilation order for McCabe sources VMS 4.0 McCabe executable Ada source statement count This file The [.SOURCE] subdirectory contains most of the sources that make up the McCabe tool. Other sources are contained in the abstractions directory. Files in [.SOURCE]: The source files which make up the McCabe tool are as follows: MCCGRMLP.ADA units) MCCGRMAA.SUB FLOWGRAPH.SPC (spec) FLOWGRAPH.BDY (body) GETNEXT.SUB -- Lexer/parser source code (contains several program -- Action routine created from the LALR grammar -- Subprograms used to build and access the Flow Graph -- Subprograms used to build and access the Flow Graph -- Subprogram to obtain lexer tokens Page 7 MCCGRMC.SPC (spec) MCCGRMC.BDY (body) MCCABE.ADA MCCDEFS.SPC variables -- Grammar constants (created from the LALR grammar) -- Grammar constants (created from the LALR grammar) -- McCabe Complexity Driver -- McCabeDefinitions containing global types and used by the various parts of the McCabe Complexity Tool MCCSTK.SPC (spec) MCCSTK.BDY (body) PARSE.BDY MCCGRMPT.SPC MCCGRMPT.BDY STATEMENT.SPC STATEMENT.BDY -- Subprograms to keep account of nodes and edges -- Subprograms to keep account of nodes and edges ------ Parser for the McCabe utility. ParserTables created from the LALR grammar (spec) ParserTables created from the LALR grammar (body) Subprograms to create the flow graph (spec) Subprograms to create the flow graph (body) The [.TEST] subdirectory contains tests used in testing the McCabe tool. Files in [.TEST]: TMCCABE.COM -- COM file to run all test for McCabe tool CHECKOUT.COM -- COM file to compare old result with the new (after running TMCCABE.COM) CLEANUP.COM -- COM file to clean the [.TEST] directory Files with file type .TST are inputs to the McCabe test. EXCEPT.TST LABS6.TST SELECT.TST TEST10A.TST TEST18.TST TEST24.TST TEST7.TST LABS.TST LOOP.TST SELECT1.TST TEST11A.TST TEST2.TST TEST3.TST TEST7A.TST LABS2.TST MLABS.TST SELECT3.TST TEST12.TST TEST21.TST TEST4.TST TEST8.TST LABS4.TST MLABS5.TST TEST1.TST TEST13.TST TEST22.TST TEST5.TST TEST9.TST LABS5.TST MLABS6.TST TEST10.TST TEST17.TST TEST23.TST TEST6.TST Files with file type .RPT are expected results of the McCabe test. EXCEPT.RPT LABS6.RPT SELECT.RPT TEST10A.RPT TEST18.RPT TEST24.RPT TEST7.RPT LABS.RPT LOOP.RPT SELECT1.RPT TEST11A.RPT TEST2.RPT TEST3.RPT TEST7A.RPT LABS2.RPT MLABS.RPT SELECT3.RPT TEST12.RPT TEST21.RPT TEST4.RPT TEST8.RPT LABS4.RPT MLABS5.RPT TEST1.RPT TEST13.RPT TEST22.RPT TEST5.RPT TEST9.RPT LABS5.RPT MLABS6.RPT TEST10.RPT TEST17.RPT TEST23.RPT TEST6.RPT The [.DOC] subdirectory contains the documentation for the McCabe tool. Files in [.DOC]: MCCABE.MEM MCCABE.RNO MCCGRM.LIS -- McCabe tool user's manual -- Runoff input for McCabe tool user's manual -- Listing of requirements reference grammar. The rule numbers in this file may be used to make changes to the RELEASE.NTS case statement in the MCCAPPLYACTIONS subunit. -- Release notes