diff options
author | Chris Johns <chrisj@rtems.org> | 2014-05-09 21:50:37 +1000 |
---|---|---|
committer | Chris Johns <chrisj@rtems.org> | 2014-06-18 16:48:08 +1200 |
commit | 100f517ab37265acdf067e36b6860020ec8b2184 (patch) | |
tree | 2316c8b888e11dcbcfbfc66a0c1e31991ea20656 /tester/covoar/covoar.cc | |
parent | 4.11: Add ntp patch. (diff) | |
download | rtems-tools-100f517ab37265acdf067e36b6860020ec8b2184.tar.bz2 |
covoar: Merger the covoar source from rtems-testing.git.
Use waf to build covoar.
Diffstat (limited to 'tester/covoar/covoar.cc')
-rw-r--r-- | tester/covoar/covoar.cc | 507 |
1 files changed, 507 insertions, 0 deletions
diff --git a/tester/covoar/covoar.cc b/tester/covoar/covoar.cc new file mode 100644 index 0000000..78954fd --- /dev/null +++ b/tester/covoar/covoar.cc @@ -0,0 +1,507 @@ +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <list> +#include <netdb.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "app_common.h" +#include "CoverageFactory.h" +#include "CoverageMap.h" +#include "DesiredSymbols.h" +#include "ExecutableInfo.h" +#include "Explanations.h" +#include "ObjdumpProcessor.h" +#include "ReportsBase.h" +#include "TargetFactory.h" +#include "GcovData.h" + +/* + * Variables to control general behavior + */ +const char* coverageFileExtension = NULL; +std::list<std::string> coverageFileNames; +int coverageExtensionLength = 0; +Coverage::CoverageFormats_t coverageFormat; +Coverage::CoverageReaderBase* coverageReader = NULL; +char* executable = NULL; +const char* executableExtension = NULL; +int executableExtensionLength = 0; +std::list<Coverage::ExecutableInfo*> executablesToAnalyze; +const char* explanations = NULL; +char* progname; +const char* symbolsFile = NULL; +const char* gcnosFileName = NULL; +char gcnoFileName[FILE_NAME_LENGTH]; +char gcdaFileName[FILE_NAME_LENGTH]; +char gcovBashCommand[256]; +const char* target = NULL; +const char* format = NULL; +FILE* gcnosFile = NULL; +Gcov::GcovData* gcovFile; + +/* + * Print program usage message + */ +void usage() +{ + fprintf( + stderr, + "Usage: %s [-v] -T TARGET -f FORMAT [-E EXPLANATIONS] -1 EXECUTABLE coverage1 ... coverageN\n" + "--OR--\n" + "Usage: %s [-v] -T TARGET -f FORMAT [-E EXPLANATIONS] -e EXE_EXTENSION -c COVERAGEFILE_EXTENSION EXECUTABLE1 ... EXECUTABLE2\n" + "\n" + " -v - verbose at initialization\n" + " -T TARGET - target name\n" + " -f FORMAT - coverage file format " + "(RTEMS, QEMU, TSIM or Skyeye)\n" + " -E EXPLANATIONS - name of file with explanations\n" + " -s SYMBOLS_FILE - name of file with symbols of interest\n" + " -1 EXECUTABLE - name of executable to get symbols from\n" + " -e EXE_EXTENSION - extension of the executables to analyze\n" + " -c COVERAGEFILE_EXTENSION - extension of the coverage files to analyze\n" + " -g GCNOS_LIST - name of file with list of *.gcno files\n" + " -p PROJECT_NAME - name of the project\n" + " -C ConfigurationFileName - name of configuration file\n" + " -O Output_Directory - name of output directory (default=." + "\n", + progname, + progname + ); +} + +#define PrintableString(_s) \ + ((!(_s)) ? "NOT SET" : (_s)) + +/* + * Configuration File Support + */ +#include "ConfigFile.h" +Configuration::FileReader *CoverageConfiguration; +Configuration::Options_t Options[] = { + { "explanations", NULL }, + { "format", NULL }, + { "symbolsFile", NULL }, + { "outputDirectory", NULL }, + { "executableExtension", NULL }, + { "coverageExtension", NULL }, + { "gcnosFile", NULL }, + { "target", NULL }, + { "verbose", NULL }, + { "projectName", NULL }, + { NULL, NULL } +}; + +bool isTrue(const char *value) +{ + if ( !value ) return false; + if ( !strcmp(value, "true") ) return true; + if ( !strcmp(value, "TRUE") ) return true; + if ( !strcmp(value, "yes") ) return true; + if ( !strcmp(value, "YES") ) return true; + return false; +} + +#define GET_BOOL(_opt, _val) \ + if (isTrue(CoverageConfiguration->getOption(_opt))) \ + _val = true; + +#define GET_STRING(_opt, _val) \ + do { \ + const char *_t; \ + _t = CoverageConfiguration->getOption(_opt); \ + if ( _t ) _val = _t; \ + } while(0) + + +void check_configuration(void) +{ + GET_BOOL( "verbose", Verbose ); + + GET_STRING( "format", format ); + GET_STRING( "target", target ); + GET_STRING( "explanations", explanations ); + GET_STRING( "symbolsFile", symbolsFile ); + GET_STRING( "outputDirectory", outputDirectory ); + GET_STRING( "executableExtension", executableExtension ); + GET_STRING( "coverageExtension", coverageFileExtension ); + GET_STRING( "gcnosFile", gcnosFileName ); + GET_STRING( "projectName", projectName ); + + // Now calculate some values + if ( coverageFileExtension ) + coverageExtensionLength = strlen( coverageFileExtension ); + + if ( executableExtension ) + executableExtensionLength = strlen( executableExtension ); + + if ( format ) + coverageFormat = Coverage::CoverageFormatToEnum( format ); +} + +int main( + int argc, + char** argv +) +{ + std::list<std::string>::iterator citr; + std::string coverageFileName; + std::list<Coverage::ExecutableInfo*>::iterator eitr; + Coverage::ExecutableInfo* executableInfo = NULL; + int i; + int opt; + const char* singleExecutable = NULL; + + CoverageConfiguration = new Configuration::FileReader(Options); + + // + // Process command line options. + // + progname = argv[0]; + + while ((opt = getopt(argc, argv, "C:1:L:e:c:g:E:f:s:T:O:p:v")) != -1) { + switch (opt) { + case 'C': CoverageConfiguration->processFile( optarg ); break; + case '1': singleExecutable = optarg; break; + case 'L': dynamicLibrary = optarg; break; + case 'e': executableExtension = optarg; break; + case 'c': coverageFileExtension = optarg; break; + case 'g': gcnosFileName = optarg; break; + case 'E': explanations = optarg; break; + case 'f': format = optarg; break; + case 's': symbolsFile = optarg; break; + case 'T': target = optarg; break; + case 'O': outputDirectory = optarg; break; + case 'v': Verbose = true; break; + case 'p': projectName = optarg; break; + default: /* '?' */ + usage(); + exit( -1 ); + } + } + + // Do not trust any arguments until after this point. + check_configuration(); + + // XXX We need to verify that all of the needed arguments are non-NULL. + + // If a single executable was specified, process the remaining + // arguments as coverage file names. + if (singleExecutable) { + + // Ensure that the executable is readable. + if (!FileIsReadable( singleExecutable )) { + fprintf( + stderr, + "WARNING: Unable to read executable %s\n", + singleExecutable + ); + } + + else { + + for (i=optind; i < argc; i++) { + + // Ensure that the coverage file is readable. + if (!FileIsReadable( argv[i] )) { + fprintf( + stderr, + "WARNING: Unable to read coverage file %s\n", + argv[i] + ); + } + + else + coverageFileNames.push_back( argv[i] ); + } + + // If there was at least one coverage file, create the + // executable information. + if (!coverageFileNames.empty()) { + if (dynamicLibrary) + executableInfo = new Coverage::ExecutableInfo( + singleExecutable, dynamicLibrary + ); + else + executableInfo = new Coverage::ExecutableInfo( singleExecutable ); + + executablesToAnalyze.push_back( executableInfo ); + } + } + } + + // If not invoked with a single executable, process the remaining + // arguments as executables and derive the coverage file names. + else { + for (i = optind; i < argc; i++) { + + // Ensure that the executable is readable. + if (!FileIsReadable( argv[i] )) { + fprintf( + stderr, + "WARNING: Unable to read executable %s\n", + argv[i] + ); + } + + else { + coverageFileName = argv[i]; + coverageFileName.replace( + coverageFileName.length() - executableExtensionLength, + executableExtensionLength, + coverageFileExtension + ); + + if (!FileIsReadable( coverageFileName.c_str() )) { + fprintf( + stderr, + "WARNING: Unable to read coverage file %s\n", + coverageFileName.c_str() + ); + } + + else { + executableInfo = new Coverage::ExecutableInfo( argv[i] ); + executablesToAnalyze.push_back( executableInfo ); + coverageFileNames.push_back( coverageFileName ); + } + } + } + } + + // Ensure that there is at least one executable to process. + if (executablesToAnalyze.empty()) { + fprintf( + stderr, "ERROR: No information to analyze\n" + ); + exit( -1 ); + } + + if (Verbose) { + if (singleExecutable) + fprintf( + stderr, + "Processing a single executable and multiple coverage files\n" + ); + else + fprintf( + stderr, + "Processing multiple executable/coverage file pairs\n" + ); + fprintf( stderr, "Coverage Format : %s\n", format ); + fprintf( stderr, "Target : %s\n", PrintableString(target) ); + fprintf( stderr, "\n" ); +#if 1 + // Process each executable/coverage file pair. + eitr = executablesToAnalyze.begin(); + for (citr = coverageFileNames.begin(); + citr != coverageFileNames.end(); + citr++) { + + fprintf( + stderr, + "Coverage file %s for executable %s\n", + (*citr).c_str(), + ((*eitr)->getFileName()).c_str() + ); + + if (!singleExecutable) + eitr++; + } +#endif + } + + // + // Validate inputs. + // + + // Target name must be set. + if (!target) { + fprintf( stderr, "ERROR: target not specified\n" ); + usage(); + exit(-1); + } + + // Validate format. + if (!format) { + fprintf( stderr, "ERROR: coverage format report not specified\n" ); + usage(); + exit(-1); + } + + // Validate that we have a symbols of interest file. + if (!symbolsFile) { + fprintf( stderr, "ERROR: symbols of interest file not specified\n" ); + usage(); + exit(-1); + } + + // + // Create data to support analysis. + // + + // Create data based on target. + TargetInfo = Target::TargetFactory( target ); + + // Create the set of desired symbols. + SymbolsToAnalyze = new Coverage::DesiredSymbols(); + SymbolsToAnalyze->load( symbolsFile ); + if (Verbose) + fprintf( + stderr, + "Analyzing %u symbols\n", + (unsigned int) SymbolsToAnalyze->set.size() + ); + + // Create explanations. + AllExplanations = new Coverage::Explanations(); + if ( explanations ) + AllExplanations->load( explanations ); + + // Create coverage map reader. + coverageReader = Coverage::CreateCoverageReader(coverageFormat); + if (!coverageReader) { + fprintf( stderr, "ERROR: Unable to create coverage file reader\n" ); + exit(-1); + } + + // Create the objdump processor. + objdumpProcessor = new Coverage::ObjdumpProcessor(); + + // Prepare each executable for analysis. + for (eitr = executablesToAnalyze.begin(); + eitr != executablesToAnalyze.end(); + eitr++) { + + if (Verbose) + fprintf( + stderr, + "Extracting information from %s\n", + ((*eitr)->getFileName()).c_str() + ); + + // If a dynamic library was specified, determine the load address. + if (dynamicLibrary) + (*eitr)->setLoadAddress( + objdumpProcessor->determineLoadAddress( *eitr ) + ); + + // Load the objdump for the symbols in this executable. + objdumpProcessor->load( *eitr ); + } + + // + // Analyze the coverage data. + // + + // Process each executable/coverage file pair. + eitr = executablesToAnalyze.begin(); + for (citr = coverageFileNames.begin(); + citr != coverageFileNames.end(); + citr++) { + + if (Verbose) + fprintf( + stderr, + "Processing coverage file %s for executable %s\n", + (*citr).c_str(), + ((*eitr)->getFileName()).c_str() + ); + + // Process its coverage file. + coverageReader->processFile( (*citr).c_str(), *eitr ); + + // Merge each symbols coverage map into a unified coverage map. + (*eitr)->mergeCoverage(); + + // DEBUG Print ExecutableInfo content + //(*eitr)->dumpExecutableInfo(); + + if (!singleExecutable) + eitr++; + } + + // Do necessary preprocessing of uncovered ranges and branches + if (Verbose) + fprintf( stderr, "Preprocess uncovered ranges and branches\n" ); + SymbolsToAnalyze->preprocess(); + + // + // Generate Gcov reports + // + if (Verbose) + fprintf( stderr, "Generating Gcov reports...\n"); + gcnosFile = fopen ( gcnosFileName , "r" ); + + if ( !gcnosFile ) { + fprintf( stderr, "Unable to open %s\n", gcnosFileName ); + } + else { + while ( fscanf( gcnosFile, "%s", inputBuffer ) != EOF) { + gcovFile = new Gcov::GcovData(); + strcpy( gcnoFileName, inputBuffer ); + + if ( Verbose ) + fprintf( stderr, "Processing file: %s\n", gcnoFileName ); + + if ( gcovFile->readGcnoFile( gcnoFileName ) ) { + // Those need to be in this order + gcovFile->processCounters(); + gcovFile->writeReportFile(); + gcovFile->writeGcdaFile(); + gcovFile->writeGcovFile(); + } + + delete gcovFile; + } + fclose( gcnosFile ); + } + + // Determine the uncovered ranges and branches. + if (Verbose) + fprintf( stderr, "Computing uncovered ranges and branches\n" ); + SymbolsToAnalyze->computeUncovered(); + + // Calculate remainder of statistics. + if (Verbose) + fprintf( stderr, "Calculate statistics\n" ); + SymbolsToAnalyze->calculateStatistics(); + + // Look up the source lines for any uncovered ranges and branches. + if (Verbose) + fprintf( + stderr, "Looking up source lines for uncovered ranges and branches\n" + ); + SymbolsToAnalyze->findSourceForUncovered(); + + // + // Report the coverage data. + // + if (Verbose) + fprintf( + stderr, "Generate Reports\n" + ); + Coverage::GenerateReports(); + + // Write explanations that were not found. + if ( explanations ) { + std::string notFound; + + notFound = outputDirectory; + notFound += "/"; + notFound += "ExplanationsNotFound.txt"; + + if (Verbose) + fprintf( stderr, "Writing Not Found Report (%s)\n", notFound.c_str() ); + AllExplanations->writeNotFound( notFound.c_str() ); + } + + return 0; +} |