Did you know ... | Search Documentation: |
Pack logtalk -- logtalk-3.86.0/manuals/_sources/devtools/code_metrics.rst.txt |
.. _library_code_metrics:
code_metrics
The purpose of this tool is to assess qualities of source code that may predict negative aspects such as entity coupling, cohesion, complexity, error-proneness, and overall maintainability. It is meant to be extensible via the addition of objects implementing new metrics.
This tool provides predicates for computing metrics for source files, entities, libraries, files, and directories. The actual availability of a particular predicate depends on the specific metric. A set of predicates prints, by default, the computed metric values to the standard output. A second set of predicates computes and returns a score (usually a compound term with the computed metric values as arguments).
This tool API documentation is available at:
`../../docs/library_index.html#code-metrics <../../docs/library_index.html#code-metrics>`__
This tool can be loaded using the query:
::
| ?- logtalk_load(code_metrics(loader))
.
To test this tool, load the tester.lgt
file:
::
| ?- logtalk_load(code_metrics(tester))
.
Currently, the following metrics are provided:
noc_metric
)nor_metric
)upn_metric
)cc_metric
)dit_metric
)coupling_metric
)doc_metric
)size_metric
)halstead_metric
and
halstead_metric(Stroud)
)
A helper object, code_metrics
, is also provided, allowing running
all loaded individual metrics. For code coverage metrics, see the
lgtunit
tool documentation.
Ce
): Number of entities that an entity depends
on. These include objects receiving messages from the entity, plus the
implemented protocols, imported categories, and
extended/instantiated/specialized objects.Ca
): Number of entities that depend on an
entity. For a protocol, the number of protocols that extend it, plus
the number of objects and categories that implement it. For a
category, the number of objects that import it. For an object, the
number of categories and objects that send messages to it plus the
number of objects that extend/instantiate/specialize it.Ce / (Ce + Ca)
. Measures an entity
resilience to change. Ranging from 0.0 to 1.0, with 0.0 indicating a
maximally stable entity and 1.0 indicating a maximally unstable
entity. Ideally, an entity is either maximally stable or maximally
unstable.The dependencies count includes direct entity relations plus predicate calls or dynamic updates to predicates in external objects or categories.
For more information on the interpretation of the coupling metric scores, see, e.g., the original paper by Robert Martin:
::
@inproceedings{citeulike:1579528, author = "Martin, Robert", booktitle = "Workshop Pragmatic and Theoretical Directions in Object-Oriented Software Metrics", citeulike-article-id = 1579528, citeulike-linkout-0 = "http://www.objectmentor.com/resources/articles/oodmetrc.pdf", keywords = "diplomarbeit", organization = "OOPSLA'94", posted-at = "2007-08-21 11:08:44", priority = 0, title = "OO Design Quality Metrics - An Analysis of Dependencies", url = "http://www.objectmentor.com/resources/articles/oodmetrc.pdf", year = 1994 }
The coupling metric was also influenced by the metrics rating system in Microsoft Visual Studio and aims to eventually emulate the functionality of a maintainability index score.
Predicates declared, user-defined, and called are interpreted as operators. Built-in predicates and built-in control constructs are ignored. Predicate arguments are abstracted, assumed distinct, and interpreted as operands. Note that this definition of operands is a significant deviation from the original definition, which used syntactic literals. A computation closer to the original definition of the metric would require switching to use the parser to collect information on syntactic literals, which would imply a much larger computation cost.
The computation of this metric is parameterized by the Stroud coefficient for computing the time required to program (default is 18). The following individual measures are computed:
Pn
).PAn
).Cn
).CAn
).EV
). Computed as EV = Pn + PAn
.EL
). Computed as EL = Cn + CAn
.V
). Computed as V = EL * log2(EV)
.D
). Computed as D = (Pn/2) * (CAn/An)
.E
). Computed as E = D * V
.T
). Computed as T = E/k
seconds
(where k
is the Stroud number; defaults to 18).B
). Computed as B = V/3000
.The Unique Predicate Nodes (UPN) metric is described in the following paper:
::
@article{MOORES199845, title = "Applying Complexity Measures to Rule-Based Prolog Programs", journal = "Journal of Systems and Software", volume = "44", number = "1", pages = "45 - 52", year = "1998", issn = "0164-1212", doi = "https://doi.org/10.1016/S0164-1212(98)10042-0", url = "http://www.sciencedirect.com/science/article/pii/S0164121298100420", author = "Trevor T Moores" }
The nodes include called and updated predicates independently of where they are defined. It also includes multifile predicates contributed to other entities.
The cyclomatic complexity metric evaluates an entity code complexity by measuring the number of linearly independent paths through the code. In its current implementation, all defined predicates that are not called or updated are counted as graph-connected components (the reasoning being that these predicates can be considered entry points). The implementation uses the same predicate abstraction as the UPN metric. The defined predicates include multifile predicate definitions contributed by the entity to other entities.
For more details on this metric, see the original paper by Thomas J. McCabe:
::
@inproceedings{McCabe:1976:CM:800253.807712, author = "McCabe, Thomas J.", title = "A Complexity Measure", booktitle = "Proceedings of the 2Nd International Conference on Software Engineering", series = "ICSE '76", year = 1976, location = "San Francisco, California, USA", pages = "407--", url = "http://dl.acm.org/citation.cfm?id=800253.807712", acmid = 807712, publisher = "IEEE Computer Society Press", address = "Los Alamitos, CA, USA", keywords = "Basis, Complexity measure, Control flow, Decomposition, Graph theory, Independence, Linear, Modularization, Programming, Reduction, Software, Testing", }
All metrics require the source code to be analyzed to be loaded with the
source_data
flag turned on. For usage examples, see the
SCRIPT.txt
file in the tool directory.
Be sure to fully understand the metrics individual meanings and any implementation limitations before using them to support any evaluation or decision process.
A set of options is available to specify code that should be excluded when applying code metrics:
exclude_directories(Directories)
| list of directories to exclude (default is []
); all
sub-directories of the excluded directories are also excluded;
directories may be listed by full or relative pathexclude_files(Files)
| list of source files to exclude (default is []
); files may be
listed by full path or basename, with or without extensionexclude_libraries(Libraries)
| list of libraries to exclude (default is
[startup, scratch_directory]
)exclude_entities(Entities)
| list of entities to exclude (default is []
)
New metrics can be implemented by defining an object that imports the
code_metric
category and implements its score predicates. There is
also a code_metrics_utilities
category that defines useful
predicates for the definition of metrics.
The following open-source command-line programs can count blank lines, comment lines, and lines of source code in many programming languages, including Logtalk:
cloc
- https://github.com/AlDanial/clocohcount
- https://github.com/blackducksoftware/ohcounttokei
- https://github.com/XAMPPRocky/tokei
Some of the metrics can also be applied to Prolog modules that Logtalk
is able to compile as objects. For example, if the Prolog module file is
named module.pl
, try:
::
| ?- logtalk_load(module, [source_data(on)])
.
Due to the lack of standardization of module systems and the abundance of proprietary extensions, this solution is not expected to work for all cases.
Some of the metrics can also be applied to plain Prolog code. For
example, if the Prolog file is named code.pl
, simply define an
object including its code:
::
:- object(code)
.
:- include('code.pl')
.
:- end_object.
Save the object to an e.g. code.lgt
file in the same directory as
the Prolog file and then load it in debug mode:
::
| ?- logtalk_load(code, [source_data(on)])
.
In alternative, use the object_wrapper_hook
provided by the
hook_objects
library:
::
| ?- logtalk_load(hook_objects(loader))
.
...
| ?- logtalk_load(code, [hook(object_wrapper_hook), source_data(on)])
.
With either wrapping solution, pay special attention to any compilation warnings that may signal issues that could prevent the plain Prolog code from working when wrapped by an object.