#include #include #include #include #include #include "ReportsBase.h" #include "app_common.h" #include "CoverageRanges.h" #include "DesiredSymbols.h" #include "Explanations.h" #include "ObjdumpProcessor.h" #include "ReportsText.h" #include "ReportsHtml.h" #ifdef _WIN32 #include #endif namespace Coverage { ReportsBase::ReportsBase( time_t timestamp ): reportExtension_m(""), timestamp_m( timestamp ) { } ReportsBase::~ReportsBase() { } FILE* ReportsBase::OpenFile( const char* const fileName ) { int sc; FILE *aFile; std::string file; // Create the output directory if it does not already exist #ifdef _WIN32 sc = _mkdir( outputDirectory ); #else sc = mkdir( outputDirectory,0755 ); #endif if ( (sc == -1) && (errno != EEXIST) ) { fprintf(stderr, "Unable to create output directory %s\n", outputDirectory); return NULL; } file = outputDirectory; file += "/"; file += fileName; // Open the file. aFile = fopen( file.c_str(), "w" ); if ( !aFile ) { fprintf( stderr, "Unable to open %s\n", file.c_str() ); } return aFile; } void ReportsBase::WriteIndex( const char* const fileName ) { } FILE* ReportsBase::OpenAnnotatedFile( const char* const fileName ) { return OpenFile(fileName); } FILE* ReportsBase::OpenBranchFile( const char* const fileName, bool hasBranches ) { return OpenFile(fileName); } FILE* ReportsBase::OpenCoverageFile( const char* const fileName ) { return OpenFile(fileName); } FILE* ReportsBase::OpenNoRangeFile( const char* const fileName ) { return OpenFile(fileName); } FILE* ReportsBase::OpenSizeFile( const char* const fileName ) { return OpenFile(fileName); } FILE* ReportsBase::OpenSymbolSummaryFile( const char* const fileName ) { return OpenFile(fileName); } void ReportsBase::CloseFile( FILE* aFile ) { fclose( aFile ); } void ReportsBase::CloseAnnotatedFile( FILE* aFile ) { CloseFile( aFile ); } void ReportsBase::CloseBranchFile( FILE* aFile, bool hasBranches ) { CloseFile( aFile ); } void ReportsBase::CloseCoverageFile( FILE* aFile ) { CloseFile( aFile ); } void ReportsBase::CloseNoRangeFile( FILE* aFile ) { CloseFile( aFile ); } void ReportsBase::CloseSizeFile( FILE* aFile ) { CloseFile( aFile ); } void ReportsBase::CloseSymbolSummaryFile( FILE* aFile ) { CloseFile( aFile ); } std::string expand_tabs(const std::string& in) { std::string expanded = ""; int i = 0; for (char c : in) { if (c == '\t') { int num_tabs = 4 - (i % 4); expanded.append(num_tabs, ' '); i += num_tabs; } else { expanded += c; i++; } } return expanded; } /* * Write annotated report */ void ReportsBase::WriteAnnotatedReport( const char* const fileName ) { FILE* aFile = NULL; Coverage::DesiredSymbols::symbolSet_t::iterator ditr; Coverage::CoverageRanges* theBranches; Coverage::CoverageRanges* theRanges; Coverage::CoverageMapBase* theCoverageMap = NULL; uint32_t bAddress = 0; AnnotatedLineState_t state; std::list* theInstructions; std::list::iterator itr; aFile = OpenAnnotatedFile(fileName); if (!aFile) return; // Process uncovered branches for each symbol. for (ditr = SymbolsToAnalyze->set.begin(); ditr != SymbolsToAnalyze->set.end(); ditr++) { // If uncoveredRanges and uncoveredBranches don't exist, then the // symbol was never referenced by any executable. Just skip it. if ((ditr->second.uncoveredRanges == NULL) && (ditr->second.uncoveredBranches == NULL)) continue; // If uncoveredRanges and uncoveredBranches are empty, then everything // must have been covered for this symbol. Just skip it. if ((ditr->second.uncoveredRanges->set.empty()) && (ditr->second.uncoveredBranches->set.empty())) continue; theCoverageMap = ditr->second.unifiedCoverageMap; bAddress = ditr->second.baseAddress; theInstructions = &(ditr->second.instructions); theRanges = ditr->second.uncoveredRanges; theBranches = ditr->second.uncoveredBranches; // Add annotations to each line where necessary AnnotatedStart( aFile ); for (itr = theInstructions->begin(); itr != theInstructions->end(); itr++ ) { uint32_t id = 0; std::string annotation = ""; std::string line; const std::size_t LINE_LENGTH = 150; char textLine[LINE_LENGTH]; state = A_SOURCE; if ( itr->isInstruction ) { if (!theCoverageMap->wasExecuted( itr->address - bAddress )){ annotation = "<== NOT EXECUTED"; state = A_NEVER_EXECUTED; id = theRanges->getId( itr->address ); } else if (theCoverageMap->isBranch( itr->address - bAddress )) { id = theBranches->getId( itr->address ); if (theCoverageMap->wasAlwaysTaken( itr->address - bAddress )){ annotation = "<== ALWAYS TAKEN"; state = A_BRANCH_TAKEN; } else if (theCoverageMap->wasNeverTaken( itr->address - bAddress )){ annotation = "<== NEVER TAKEN"; state = A_BRANCH_NOT_TAKEN; } } else { state = A_EXECUTED; } } std::string textLineWithoutTabs = expand_tabs(itr->line); snprintf( textLine, LINE_LENGTH, "%-90s", textLineWithoutTabs.c_str() ); line = textLine + annotation; PutAnnotatedLine( aFile, state, line, id); } AnnotatedEnd( aFile ); } CloseAnnotatedFile( aFile ); } /* * Write branch report */ void ReportsBase::WriteBranchReport( const char* const fileName ) { Coverage::DesiredSymbols::symbolSet_t::iterator ditr; FILE* report = NULL; Coverage::CoverageRanges::ranges_t::iterator ritr; Coverage::CoverageRanges* theBranches; unsigned int count; bool hasBranches = true; if ((SymbolsToAnalyze->getNumberBranchesFound() == 0) || (BranchInfoAvailable == false) ) hasBranches = false; // Open the branch report file report = OpenBranchFile( fileName, hasBranches ); if (!report) return; // If no branches were found then branch coverage is not supported if ((SymbolsToAnalyze->getNumberBranchesFound() != 0) && (BranchInfoAvailable == true) ) { // Process uncovered branches for each symbol. count = 0; for (ditr = SymbolsToAnalyze->set.begin(); ditr != SymbolsToAnalyze->set.end(); ditr++) { theBranches = ditr->second.uncoveredBranches; if (theBranches && !theBranches->set.empty()) { for (ritr = theBranches->set.begin() ; ritr != theBranches->set.end() ; ritr++ ) { count++; PutBranchEntry( report, count, ditr, ritr ); } } } } CloseBranchFile( report, hasBranches ); } /* * Write coverage report */ void ReportsBase::WriteCoverageReport( const char* const fileName ) { Coverage::DesiredSymbols::symbolSet_t::iterator ditr; FILE* report; Coverage::CoverageRanges::ranges_t::iterator ritr; Coverage::CoverageRanges* theRanges; unsigned int count; FILE* NoRangeFile; std::string NoRangeName; // Open special file that captures NoRange informaiton NoRangeName = "no_range_"; NoRangeName += fileName; NoRangeFile = OpenNoRangeFile ( NoRangeName.c_str() ); if (!NoRangeFile) { return; } // Open the coverage report file. report = OpenCoverageFile( fileName ); if ( !report ) { return; } // Process uncovered ranges for each symbol. count = 0; for (ditr = SymbolsToAnalyze->set.begin(); ditr != SymbolsToAnalyze->set.end(); ditr++) { theRanges = ditr->second.uncoveredRanges; // If uncoveredRanges doesn't exist, then the symbol was never // referenced by any executable. There may be a problem with the // desired symbols list or with the executables so put something // in the report. if (theRanges == NULL) { putCoverageNoRange( report, NoRangeFile, count, ditr->first ); count++; } else if (!theRanges->set.empty()) { for (ritr = theRanges->set.begin() ; ritr != theRanges->set.end() ; ritr++ ) { PutCoverageLine( report, count, ditr, ritr ); count++; } } } CloseNoRangeFile( NoRangeFile ); CloseCoverageFile( report ); } /* * Write size report */ void ReportsBase::WriteSizeReport( const char* const fileName ) { Coverage::DesiredSymbols::symbolSet_t::iterator ditr; FILE* report; Coverage::CoverageRanges::ranges_t::iterator ritr; Coverage::CoverageRanges* theRanges; unsigned int count; // Open the report file. report = OpenSizeFile( fileName ); if ( !report ) { return; } // Process uncovered ranges for each symbol. count = 0; for (ditr = SymbolsToAnalyze->set.begin(); ditr != SymbolsToAnalyze->set.end(); ditr++) { theRanges = ditr->second.uncoveredRanges; if (theRanges && !theRanges->set.empty()) { for (ritr = theRanges->set.begin() ; ritr != theRanges->set.end() ; ritr++ ) { PutSizeLine( report, count, ditr, ritr ); count++; } } } CloseSizeFile( report ); } void ReportsBase::WriteSymbolSummaryReport( const char* const fileName ) { Coverage::DesiredSymbols::symbolSet_t::iterator ditr; FILE* report; unsigned int count; // Open the report file. report = OpenSymbolSummaryFile( fileName ); if ( !report ) { return; } // Process each symbol. count = 0; for (ditr = SymbolsToAnalyze->set.begin(); ditr != SymbolsToAnalyze->set.end(); ditr++) { PutSymbolSummaryLine( report, count, ditr ); count++; } CloseSymbolSummaryFile( report ); } void ReportsBase::WriteSummaryReport( const char* const fileName ) { // Calculate coverage statistics and output results. uint32_t a; uint32_t endAddress; Coverage::DesiredSymbols::symbolSet_t::iterator itr; uint32_t notExecuted = 0; double percentage; double percentageBranches; Coverage::CoverageMapBase* theCoverageMap; uint32_t totalBytes = 0; FILE* report; // Open the report file. report = OpenFile( fileName ); if ( !report ) { return; } // Look at each symbol. for (itr = SymbolsToAnalyze->set.begin(); itr != SymbolsToAnalyze->set.end(); itr++) { // If the symbol's unified coverage map exists, scan through it // and count bytes. theCoverageMap = itr->second.unifiedCoverageMap; if (theCoverageMap) { endAddress = itr->second.stats.sizeInBytes - 1; for (a = 0; a <= endAddress; a++) { totalBytes++; if (!theCoverageMap->wasExecuted( a )) notExecuted++; } } } percentage = (double) notExecuted; percentage /= (double) totalBytes; percentage *= 100.0; percentageBranches = (double) ( SymbolsToAnalyze->getNumberBranchesAlwaysTaken() + SymbolsToAnalyze->getNumberBranchesNeverTaken() + (SymbolsToAnalyze->getNumberBranchesNotExecuted() * 2) ); percentageBranches /= (double) SymbolsToAnalyze->getNumberBranchesFound() * 2; percentageBranches *= 100.0; fprintf( report, "Bytes Analyzed : %d\n", totalBytes ); fprintf( report, "Bytes Not Executed : %d\n", notExecuted ); fprintf( report, "Percentage Executed : %5.4g\n", 100.0 - percentage ); fprintf( report, "Percentage Not Executed : %5.4g\n", percentage ); fprintf( report, "Unreferenced Symbols : %d\n", SymbolsToAnalyze->getNumberUnreferencedSymbols() ); fprintf( report, "Uncovered ranges found : %d\n\n", SymbolsToAnalyze->getNumberUncoveredRanges() ); if ((SymbolsToAnalyze->getNumberBranchesFound() == 0) || (BranchInfoAvailable == false) ) { fprintf( report, "No branch information available\n" ); } else { fprintf( report, "Total conditional branches found : %d\n", SymbolsToAnalyze->getNumberBranchesFound() ); fprintf( report, "Total branch paths found : %d\n", SymbolsToAnalyze->getNumberBranchesFound() * 2 ); fprintf( report, "Uncovered branch paths found : %d\n", SymbolsToAnalyze->getNumberBranchesAlwaysTaken() + SymbolsToAnalyze->getNumberBranchesNeverTaken() + (SymbolsToAnalyze->getNumberBranchesNotExecuted() * 2) ); fprintf( report, " %d branches always taken\n", SymbolsToAnalyze->getNumberBranchesAlwaysTaken() ); fprintf( report, " %d branches never taken\n", SymbolsToAnalyze->getNumberBranchesNeverTaken() ); fprintf( report, " %d branch paths not executed\n", SymbolsToAnalyze->getNumberBranchesNotExecuted() * 2 ); fprintf( report, "Percentage branch paths covered : %4.4g\n", 100.0 - percentageBranches ); } } void GenerateReports() { typedef std::list reportList_t; reportList_t reportList; reportList_t::iterator ritr; std::string reportName; ReportsBase* reports; time_t timestamp; timestamp = time(NULL); /* get current cal time */ reports = new ReportsText(timestamp); reportList.push_back(reports); reports = new ReportsHtml(timestamp); reportList.push_back(reports); for (ritr = reportList.begin(); ritr != reportList.end(); ritr++ ) { reports = *ritr; reportName = "index" + reports->ReportExtension(); if (Verbose) fprintf( stderr, "Generate %s\n", reportName.c_str() ); reports->WriteIndex( reportName.c_str() ); reportName = "annotated" + reports->ReportExtension(); if (Verbose) fprintf( stderr, "Generate %s\n", reportName.c_str() ); reports->WriteAnnotatedReport( reportName.c_str() ); reportName = "branch" + reports->ReportExtension(); if (Verbose) fprintf( stderr, "Generate %s\n", reportName.c_str() ); reports->WriteBranchReport(reportName.c_str() ); reportName = "uncovered" + reports->ReportExtension(); if (Verbose) fprintf( stderr, "Generate %s\n", reportName.c_str() ); reports->WriteCoverageReport(reportName.c_str() ); reportName = "sizes" + reports->ReportExtension(); if (Verbose) fprintf( stderr, "Generate %s\n", reportName.c_str() ); reports->WriteSizeReport(reportName.c_str() ); reportName = "symbolSummary" + reports->ReportExtension(); if (Verbose) fprintf( stderr, "Generate %s\n", reportName.c_str() ); reports->WriteSymbolSummaryReport(reportName.c_str() ); } for (ritr = reportList.begin(); ritr != reportList.end(); ritr++ ) { reports = *ritr; delete reports; } ReportsBase::WriteSummaryReport( "summary.txt" ); } }