# Synopsis: generate a list of highest metrics for functions. (C/C++) # # Requires one existing Understand for C++ databases # # Categories: Project Report # # Languages: C # # Usage: sub usage($) { return << "END_USAGE"; ${ \( shift @_ ) } Usage: c_highestmetrics_functions.pl -db database -root_path path -maxcount maxcount -min_linecode linecode -min_cyclomatic min_cyclomatic -output_file file -db database Specify Understand database (required for uperl, inherited from Understand) -root_path path Specify a path for files to take into account () -maxcount maxcount Specify the max number of functions per categories (defaut=10) -min_linecode linecode Specify the min number of lines of code (default=100) -min_cyclomatic linecode Specify the min cyclomatic complexity (default=15) -output_file file Specify the name of the output file END_USAGE } # For the latest Understand perl API documentation, see # http://www.scitools.com/perl.html # use Understand; use Tie::RefHash; use Getopt::Long; use strict; my $dbPath; my $help; my $maxcount = 10; my $root_path = ""; my $countLineCode_Threshold = 200; my $cyclomatic_Threshold = 45; my $localVariables_Threshold = 15; my $parameters_Threshold = 8; my $output_file = ""; # if option is not found in the command line, corresponding variable is not changed, and use the initialized value. GetOptions( "db=s" => \$dbPath, "maxcount=s" => \$maxcount, "root_path=s" => \$root_path, "min_linecode=s" => \$countLineCode_Threshold, "min_cyclomatic=s" => \$cyclomatic_Threshold, "min_local_variables=s" => \$localVariables_Threshold, "min_parameters=s" => \$parameters_Threshold, "output_file=s" => \$output_file, "help" => \$help, ); my $fh; my $writeInFile = 0; prepareOutput(); display("-------------------------------------------------------\n"); display("Highest metrics for functions\n\n"); display("Database : ".$dbPath."\n"); display("maxcount : ".$maxcount."\n"); display("root_path : ".$root_path."\n"); display("min_linecode : ".$countLineCode_Threshold."\n"); display("min_cyclomatic : ".$cyclomatic_Threshold."\n"); display("min_local_variables : ".$localVariables_Threshold."\n"); display("min_parameters : ".$parameters_Threshold."\n"); display("\n"); # open the database my $db=openDatabase($dbPath); my $title = "Functions"; my $kindFunctions = "function, method"; generateHighestMetrics($db, $title, $kindFunctions, lc $root_path, "CountLineCode", $countLineCode_Threshold, $maxcount); generateHighestMetrics($db, $title, $kindFunctions, lc $root_path, "Cyclomatic", $cyclomatic_Threshold, $maxcount); generateHighesCounts($db, $title, $kindFunctions, lc $root_path, \&countLocalVariables, "number of local variables", $localVariables_Threshold, $maxcount); generateHighesCounts($db, $title, $kindFunctions, lc $root_path, \&countParameters, "number of parameters", $parameters_Threshold, $maxcount); display("-------------------------------------------------------\n"); closeDatabase($db); closeOutput(); # ------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------ # subroutines # ------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------ sub openDatabase($) { my ($dbPath) = @_; my $db = Understand::Gui::db(); # path not allowed if opened by understand if ($db && $dbPath) { die "database already opened by GUI, don't use -db option\n"; } # open database if not already open if (!$db) { my $status; die usage("Error, database not specified\n\n") unless ($dbPath); ($db,$status)=Understand::open($dbPath); die "Error opening database: ",$status,"\n" if $status; } return($db); } sub closeDatabase($) { my ($db)=@_; # close database only if we opened it $db->close() if ($dbPath); } # ------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------ sub prepareOutput { if($output_file eq "") { $writeInFile = 0; } else { if(open($fh, '>', $output_file)) { $writeInFile = 1; } } } # ------------------------------------------------------------------------------------------ # Parameters # - text # ------------------------------------------------------------------------------------------ sub display { my ($text) = @_; if($writeInFile) { print $fh $text; } else { print $text; } } # ------------------------------------------------------------------------------------------ # ------------------------------------------------------------------------------------------ sub closeOutput { if($writeInFile) { close $fh; } } # ------------------------------------------------------------------------------------------ # Parameters # - db # - title # - kind # - threshold name # - metrics name # - max result count # ------------------------------------------------------------------------------------------ sub generateHighestMetrics { my ($db, $title, $kind, $root_path, $metrics, $threshold, $maxcount) = @_; # retrieve threshold option value $threshold = 1 if ($threshold < 1); # build hash of matching entities tie my %ents, 'Tie::RefHash'; my $ents; foreach my $func ($db->ents($kind)) { # keep entity if metrics > threshold my $val = $func->metric($metrics); if ($val >= $threshold) { # check if type is defined in excluded path foreach my $ref ($func->refs("definein")) { if(index(lc $ref->file->longname(), $root_path) != -1) { $ents{$func} = $val; } } } } printTitleMetric($title, $metrics, $threshold); # output, sorted by value, then by name my $cur = 1; foreach my $func (sort { $ents{$b} <=> $ents{$a} || lc($b->longname()) cmp lc($a->longname()); } keys %ents) { if($cur <= $maxcount) { printOneResult($func, $ents{$func}); } $cur++; } display("\n"); } # ------------------------------------------------------------------------------------------ # Parameters # - db # - title # - kind # - threshold name # - function that count # - description of the metric (for the log) # - max result count # ------------------------------------------------------------------------------------------ sub generateHighesCounts { my ($db, $title, $kind, $root_path, $functor, $description, $threshold, $maxcount) = @_; # retrieve threshold option value $threshold = 1 if ($threshold < 1); # build hash of matching entities tie my %ents, 'Tie::RefHash'; my $ents; foreach my $func ($db->ents($kind)) { # keep entity if metrics > threshold my $val = $functor->($func); if ($val >= $threshold) { # check if type is defined in excluded path foreach my $ref ($func->refs("definein")) { if(index(lc $ref->file->longname(), $root_path) != -1) { $ents{$func} = $val; } } } } printTitleMetric($title, $description, $threshold); # output, sorted by value, then by name my $cur = 1; foreach my $func (sort { $ents{$b} <=> $ents{$a} || lc($b->longname()) cmp lc($a->longname()); } keys %ents) { if($cur <= $maxcount) { printOneResult($func, $ents{$func}); } $cur++; } display("\n"); } # ------------------------------------------------------------------------------------------ # Parameters # - report # - title # - metrics name # - threshold # ------------------------------------------------------------------------------------------ sub printTitleMetric { my ($title, $metrics, $threshold) = @_; display("-------------------------------------------------------\n"); display($title); display(" with highest metric "); display($metrics); display(" ( > "); display($threshold); display(" ) :\n"); display("-------------------------------------------------------\n"); } # ------------------------------------------------------------------------------------------ # Parameters # - report # - entity # - metric value # ------------------------------------------------------------------------------------------ sub printOneResult { my ($entity, $value) = @_; #value in red my $str = sprintf("%5d " ,$value); display($str); # entity my $length = length($entity->longname()); $str = $entity->longname(); # if entity is not a file, print its kind if($entity->kindname() eq "File") {} else { $str .= sprintf("%*s" , 60 - $length , "("); $str .= $entity->kindname(); $str .= ")"; } display($str); display("\n"); } # ------------------------------------------------------------------------------------------ # Parameters # - func # ------------------------------------------------------------------------------------------ sub countLocalVariables { my $func = shift; my $varCount = 0; foreach my $var ($func->ents("define","object local ~member ~unknown ~unresolved")) { $varCount = $varCount + 1; } return $varCount; } # ------------------------------------------------------------------------------------------ # Parameters # - func # ------------------------------------------------------------------------------------------ sub countParameters { my $func = shift; my $paramCount = 0; foreach my $param ($func->ents("define","parameter")) { $paramCount = $paramCount + 1; } return $paramCount; }