#include #include #include #include #include #include #include #include #include #include #include #include #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 coverageFileNames; int coverageExtensionLength = 0; Coverage::CoverageFormats_t coverageFormat; Coverage::CoverageReaderBase* coverageReader = NULL; char* executable = NULL; const char* executableExtension = NULL; int executableExtensionLength = 0; std::list 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::iterator citr; std::string coverageFileName; std::list::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; }