source: rtems-tools/tester/covoar/ReportsBase.cc @ 8167ad2

Last change on this file since 8167ad2 was 8167ad2, checked in by Ryan Long <ryan.long@…>, on 12/20/21 at 17:24:40

ReportsBase?: Change raw pointer to unique_ptr

Replaced raw pointer used with ReportsBase?-derived classes to make code
cleaner and make it to where pointers do not have to be manually
deleted.

Closes #4376

  • Property mode set to 100644
File size: 17.9 KB
Line 
1#include <stdio.h>
2#include <string.h>
3#include <errno.h>
4#include <sys/stat.h>
5#include <sys/types.h>
6#include <assert.h>
7
8#include <iomanip>
9#include <sstream>
10
11#include "ReportsBase.h"
12#include "CoverageRanges.h"
13#include "DesiredSymbols.h"
14#include "Explanations.h"
15#include "ObjdumpProcessor.h"
16
17#include "ReportsText.h"
18#include "ReportsHtml.h"
19
20#ifdef _WIN32
21#include <direct.h>
22#endif
23
24namespace Coverage {
25
26ReportsBase::ReportsBase(
27  time_t                  timestamp,
28  const std::string&      symbolSetName,
29  Coverage::Explanations& allExplanations,
30  const std::string&      projectName,
31  const std::string&      outputDirectory,
32  const DesiredSymbols&   symbolsToAnalyze,
33  bool                    branchInfoAvailable
34): reportExtension_m( "" ),
35   symbolSetName_m( symbolSetName ),
36   timestamp_m( timestamp ),
37   allExplanations_m( allExplanations ),
38   projectName_m( projectName ),
39   outputDirectory_m( outputDirectory ),
40   symbolsToAnalyze_m( symbolsToAnalyze ),
41   branchInfoAvailable_m( branchInfoAvailable )
42{
43}
44
45ReportsBase::~ReportsBase()
46{
47}
48
49void ReportsBase::OpenFile(
50  const std::string& fileName,
51  const std::string& symbolSetName,
52  std::ofstream&     aFile,
53  const std::string& outputDirectory
54)
55{
56  int         sc;
57  std::string file;
58
59  std::string symbolSetOutputDirectory;
60  rld::path::path_join(
61    outputDirectory,
62    symbolSetName,
63    symbolSetOutputDirectory
64  );
65
66  // Create the output directory if it does not already exist
67#ifdef _WIN32
68  sc = _mkdir( symbolSetOutputDirectory );
69#else
70  sc = mkdir( symbolSetOutputDirectory.c_str(), 0755 );
71#endif
72  if ( ( sc == -1 ) && ( errno != EEXIST ) ) {
73    throw rld::error(
74      "Unable to create output directory",
75      "ReportsBase::OpenFile"
76    );
77    return;
78  }
79
80  file = symbolSetOutputDirectory;
81  rld::path::path_join( file, fileName, file );
82
83  // Open the file.
84  aFile.open( file );
85  if ( !aFile.is_open() ) {
86    std::cerr << "Unable to open " << file << std::endl;
87  }
88}
89
90void ReportsBase::WriteIndex( const std::string& fileName )
91{
92}
93
94void ReportsBase::OpenAnnotatedFile(
95  const std::string& fileName,
96  std::ofstream&     aFile
97)
98{
99  OpenFile( fileName, symbolSetName_m, aFile, outputDirectory_m );
100}
101
102void ReportsBase::OpenBranchFile(
103  const std::string& fileName,
104  bool               hasBranches,
105  std::ofstream&     aFile
106)
107{
108  OpenFile( fileName, symbolSetName_m, aFile, outputDirectory_m );
109}
110
111void ReportsBase::OpenCoverageFile(
112  const std::string& fileName,
113  std::ofstream&     aFile
114)
115{
116  OpenFile( fileName, symbolSetName_m, aFile, outputDirectory_m );
117}
118
119void ReportsBase::OpenNoRangeFile(
120  const std::string& fileName,
121  std::ofstream&     aFile
122)
123{
124  OpenFile( fileName, symbolSetName_m, aFile, outputDirectory_m );
125}
126
127
128void ReportsBase::OpenSizeFile(
129  const std::string& fileName,
130  std::ofstream&     aFile
131)
132{
133  OpenFile( fileName, symbolSetName_m, aFile, outputDirectory_m );
134}
135
136void ReportsBase::OpenSymbolSummaryFile(
137  const std::string& fileName,
138  std::ofstream&     aFile
139)
140{
141  OpenFile( fileName, symbolSetName_m, aFile, outputDirectory_m );
142}
143
144void ReportsBase::CloseFile( std::ofstream& aFile )
145{
146  aFile.close();
147}
148
149void ReportsBase::CloseAnnotatedFile( std::ofstream& aFile )
150{
151  CloseFile( aFile );
152}
153
154void ReportsBase::CloseBranchFile( std::ofstream& aFile, bool hasBranches )
155{
156  CloseFile( aFile );
157}
158
159void  ReportsBase::CloseCoverageFile( std::ofstream& aFile )
160{
161  CloseFile( aFile );
162}
163
164void  ReportsBase::CloseNoRangeFile( std::ofstream& aFile )
165{
166  CloseFile( aFile );
167}
168
169void  ReportsBase::CloseSizeFile( std::ofstream& aFile )
170{
171  CloseFile( aFile );
172}
173
174void  ReportsBase::CloseSymbolSummaryFile( std::ofstream& aFile )
175{
176  CloseFile( aFile );
177}
178
179std::string expand_tabs( const std::string& in ) {
180  std::string expanded = "";
181  int i = 0;
182
183  for ( char c : in ) {
184    if ( c == '\t' ) {
185      int num_tabs = 4 - ( i % 4 );
186      expanded.append( num_tabs, ' ' );
187      i += num_tabs;
188    } else {
189      expanded += c;
190      i++;
191    }
192  }
193
194  return expanded;
195}
196
197/*
198 *  Write annotated report
199 */
200void ReportsBase::WriteAnnotatedReport( const std::string& fileName )
201{
202  std::ofstream              aFile;
203  Coverage::CoverageRanges*  theBranches;
204  Coverage::CoverageRanges*  theRanges;
205  Coverage::CoverageMapBase* theCoverageMap = NULL;
206  uint32_t                   bAddress = 0;
207  AnnotatedLineState_t       state;
208
209  OpenAnnotatedFile( fileName, aFile );
210  if ( !aFile.is_open() ) {
211    throw rld::error(
212      "Unable to open " + fileName,
213      "ReportsBase::WriteAnnotatedReport"
214    );
215    return;
216  }
217
218  // Process uncovered branches for each symbol.
219  const std::vector<std::string>& symbols =
220    symbolsToAnalyze_m.getSymbolsForSet( symbolSetName_m );
221
222  for ( const auto& symbol : symbols ) {
223    const SymbolInformation& info =
224      symbolsToAnalyze_m.allSymbols().at( symbol );
225
226    // If uncoveredRanges and uncoveredBranches don't exist, then the
227    // symbol was never referenced by any executable.  Just skip it.
228    if (
229      ( info.uncoveredRanges == NULL ) &&
230      ( info.uncoveredBranches == NULL )
231    ) {
232      continue;
233    }
234
235    // uncoveredRanges and uncoveredBranches are always allocated as a pair
236    // so both are NULL or both are not NULL.
237    assert( info.uncoveredRanges != NULL && info.uncoveredBranches != NULL );
238
239    // If uncoveredRanges and uncoveredBranches are empty, then everything
240    // must have been covered for this symbol.  Just skip it.
241    if (
242      ( info.uncoveredRanges->set.empty() ) &&
243      ( info.uncoveredBranches->set.empty() )
244    ) {
245      continue;
246    }
247
248    theCoverageMap = info.unifiedCoverageMap;
249    bAddress       = info.baseAddress;
250    theRanges      = info.uncoveredRanges;
251    theBranches    = info.uncoveredBranches;
252
253    // Add annotations to each line where necessary
254    AnnotatedStart( aFile );
255    for ( const auto& instruction : info.instructions ) {
256      uint32_t          id = 0;
257      std::string       annotation = "";
258      std::string       line;
259      const std::size_t LINE_LENGTH = 150;
260      std::string       textLine = "";
261      std::stringstream ss;
262
263      state = A_SOURCE;
264
265      if ( instruction.isInstruction ) {
266        if ( !theCoverageMap->wasExecuted( instruction.address - bAddress ) ) {
267          annotation = "<== NOT EXECUTED";
268          state = A_NEVER_EXECUTED;
269          id = theRanges->getId( instruction.address );
270        } else if (
271          theCoverageMap->isBranch( instruction.address - bAddress )
272        ) {
273          id = theBranches->getId( instruction.address );
274          if (
275          theCoverageMap->wasAlwaysTaken( instruction.address - bAddress )
276        ){
277            annotation = "<== ALWAYS TAKEN";
278            state = A_BRANCH_TAKEN;
279          } else if (
280            theCoverageMap->wasNeverTaken( instruction.address - bAddress )
281          ) {
282            annotation = "<== NEVER TAKEN";
283            state = A_BRANCH_NOT_TAKEN;
284          }
285        } else {
286          state = A_EXECUTED;
287        }
288      }
289
290      std::string textLineWithoutTabs = expand_tabs( instruction.line );
291
292      ss << std::left << std::setw( 90 )
293         << textLineWithoutTabs.c_str();
294
295      textLine = ss.str().substr( 0, LINE_LENGTH );
296
297      line = textLine + annotation;
298
299      PutAnnotatedLine( aFile, state, line, id );
300    }
301
302    AnnotatedEnd( aFile );
303  }
304
305  CloseAnnotatedFile( aFile );
306}
307
308/*
309 *  Write branch report
310 */
311void ReportsBase::WriteBranchReport( const std::string& fileName )
312{
313  std::ofstream             report;
314  Coverage::CoverageRanges* theBranches;
315  unsigned int              count;
316  bool                      hasBranches = true;
317
318  if (
319    ( symbolsToAnalyze_m.getNumberBranchesFound( symbolSetName_m ) == 0 ) ||
320    ( branchInfoAvailable_m == false )
321  ) {
322     hasBranches = false;
323  }
324
325  // Open the branch report file
326  OpenBranchFile( fileName, hasBranches, report );
327  if ( !report.is_open() ) {
328    return;
329  }
330
331  // If no branches were found then branch coverage is not supported
332  if (
333    ( symbolsToAnalyze_m.getNumberBranchesFound( symbolSetName_m ) != 0 ) &&
334    ( branchInfoAvailable_m == true )
335  ) {
336    // Process uncovered branches for each symbol in the set.
337    const std::vector<std::string>& symbols =
338      symbolsToAnalyze_m.getSymbolsForSet( symbolSetName_m );
339
340    count = 0;
341    for ( const auto& symbol : symbols ) {
342      const SymbolInformation& info =
343        symbolsToAnalyze_m.allSymbols().at( symbol );
344
345      theBranches = info.uncoveredBranches;
346
347      if ( theBranches && !theBranches->set.empty() ) {
348        for ( const auto& range : theBranches->set ) {
349          count++;
350          PutBranchEntry( report, count, symbol, info, range );
351        }
352      }
353    }
354  }
355
356  CloseBranchFile( report, hasBranches );
357}
358
359/*
360 *  Write coverage report
361 */
362void ReportsBase::WriteCoverageReport( const std::string& fileName )
363{
364  std::ofstream             report;
365  Coverage::CoverageRanges* theRanges;
366  unsigned int              count;
367  std::ofstream             NoRangeFile;
368  std::string               NoRangeName;
369
370  // Open special file that captures NoRange informaiton
371  NoRangeName = "no_range_";
372  NoRangeName += fileName;
373  OpenNoRangeFile( NoRangeName, NoRangeFile );
374  if ( !NoRangeFile.is_open() ) {
375    return;
376  }
377
378  // Open the coverage report file.
379  OpenCoverageFile( fileName, report );
380  if ( !report.is_open() ) {
381    return;
382  }
383
384  // Process uncovered ranges for each symbol.
385  const std::vector<std::string>& symbols =
386    symbolsToAnalyze_m.getSymbolsForSet( symbolSetName_m );
387
388  count = 0;
389  for ( const auto& symbol : symbols ) {
390    const SymbolInformation& info =
391      symbolsToAnalyze_m.allSymbols().at( symbol );
392
393    theRanges = info.uncoveredRanges;
394
395    // If uncoveredRanges doesn't exist, then the symbol was never
396    // referenced by any executable.  There may be a problem with the
397    // desired symbols list or with the executables so put something
398    // in the report.
399    if ( theRanges == NULL ) {
400      putCoverageNoRange( report, NoRangeFile, count, symbol );
401      count++;
402    }  else if ( !theRanges->set.empty() ) {
403      for ( const auto& range : theRanges->set ) {
404        PutCoverageLine( report, count, symbol, info, range );
405        count++;
406      }
407    }
408  }
409
410  CloseNoRangeFile( NoRangeFile );
411  CloseCoverageFile( report );
412}
413
414/*
415 * Write size report
416 */
417void ReportsBase::WriteSizeReport( const std::string& fileName )
418{
419  std::ofstream             report;
420  Coverage::CoverageRanges* theRanges;
421  unsigned int              count;
422
423  // Open the report file.
424  OpenSizeFile( fileName, report );
425  if ( !report.is_open() ) {
426    return;
427  }
428
429  // Process uncovered ranges for each symbol.
430  const std::vector<std::string>& symbols =
431    symbolsToAnalyze_m.getSymbolsForSet( symbolSetName_m );
432
433  count = 0;
434  for ( const auto& symbol : symbols ) {
435    const SymbolInformation& info =
436      symbolsToAnalyze_m.allSymbols().at( symbol );
437
438    theRanges = info.uncoveredRanges;
439
440    if ( theRanges && !theRanges->set.empty() ) {
441      for ( const auto& range : theRanges->set ) {
442        PutSizeLine( report, count, symbol, range );
443        count++;
444      }
445    }
446  }
447
448  CloseSizeFile( report );
449}
450
451void ReportsBase::WriteSymbolSummaryReport(
452  const std::string&    fileName,
453  const DesiredSymbols& symbolsToAnalyze
454)
455{
456  std::ofstream report;
457  unsigned int  count;
458
459  // Open the report file.
460  OpenSymbolSummaryFile( fileName , report );
461  if ( !report.is_open() ) {
462    return;
463  }
464
465  // Process each symbol.
466  const std::vector<std::string>& symbols =
467    symbolsToAnalyze_m.getSymbolsForSet( symbolSetName_m );
468
469  count = 0;
470  for ( const auto& symbol : symbols ) {
471    const SymbolInformation& info =
472      symbolsToAnalyze_m.allSymbols().at( symbol );
473
474    PutSymbolSummaryLine( report, count, symbol, info );
475    count++;
476  }
477
478  CloseSymbolSummaryFile( report );
479}
480
481void  ReportsBase::WriteSummaryReport(
482  const std::string&              fileName,
483  const std::string&              symbolSetName,
484  const std::string&              outputDirectory,
485  const Coverage::DesiredSymbols& symbolsToAnalyze,
486  bool                            branchInfoAvailable
487)
488{
489    // Calculate coverage statistics and output results.
490  uint32_t                   a;
491  uint32_t                   endAddress;
492  uint32_t                   notExecuted = 0;
493  double                     percentage;
494  double                     percentageBranches;
495  Coverage::CoverageMapBase* theCoverageMap;
496  uint32_t                   totalBytes = 0;
497  std::ofstream              report;
498
499  // Open the report file.
500  OpenFile( fileName, symbolSetName, report, outputDirectory );
501  if ( !report.is_open() ) {
502    return;
503  }
504
505  // Look at each symbol.
506  const std::vector<std::string>& symbols =
507    symbolsToAnalyze.getSymbolsForSet( symbolSetName );
508
509  for ( const auto& symbol : symbols ) {
510    SymbolInformation info = symbolsToAnalyze.allSymbols().at( symbol );
511
512    // If the symbol's unified coverage map exists, scan through it
513    // and count bytes.
514    theCoverageMap = info.unifiedCoverageMap;
515    if ( theCoverageMap ) {
516
517      endAddress = info.stats.sizeInBytes - 1;
518
519      for ( a = 0; a <= endAddress; a++ ) {
520        totalBytes++;
521        if ( !theCoverageMap->wasExecuted( a ) )
522          notExecuted++;
523      }
524    }
525  }
526
527  if ( totalBytes == 0 ) {
528    percentage = 0;
529  } else {
530    percentage = 100.0 * (double) notExecuted / totalBytes;
531  }
532
533  percentageBranches = (double) (
534    symbolsToAnalyze.getNumberBranchesAlwaysTaken( symbolSetName ) +
535    symbolsToAnalyze.getNumberBranchesNeverTaken( symbolSetName ) +
536  ( symbolsToAnalyze.getNumberBranchesNotExecuted( symbolSetName ) * 2 )
537  );
538  percentageBranches /=
539    (double) symbolsToAnalyze.getNumberBranchesFound( symbolSetName ) * 2;
540  percentageBranches *= 100.0;
541
542  report << "Bytes Analyzed                   : " << totalBytes << std::endl
543         << "Bytes Not Executed               : " << notExecuted << std::endl
544         << "Percentage Executed              : "
545         << std::fixed << std::setprecision( 2 ) << std::setw( 5 )
546         << 100.0 - percentage << std::endl
547         << "Percentage Not Executed          : " << percentage << std::endl
548         << "Unreferenced Symbols             : "
549         << symbolsToAnalyze.getNumberUnreferencedSymbols( symbolSetName )
550         << std::endl << "Uncovered ranges found           : "
551         << symbolsToAnalyze.getNumberUncoveredRanges( symbolSetName )
552         << std::endl << std::endl;
553
554  if (
555    ( symbolsToAnalyze.getNumberBranchesFound( symbolSetName ) == 0 ) ||
556    ( branchInfoAvailable == false )
557  ) {
558    report << "No branch information available" << std::endl;
559  } else {
560    report << "Total conditional branches found : "
561           << symbolsToAnalyze.getNumberBranchesFound( symbolSetName )
562           << std::endl << "Total branch paths found         : "
563           << symbolsToAnalyze.getNumberBranchesFound( symbolSetName ) * 2
564           << std::endl << "Uncovered branch paths found     : "
565           << symbolsToAnalyze.getNumberBranchesAlwaysTaken( symbolSetName ) +
566              symbolsToAnalyze.getNumberBranchesNeverTaken( symbolSetName ) +
567            ( symbolsToAnalyze.getNumberBranchesNotExecuted( symbolSetName ) * 2 )
568           << std::endl << "   "
569           << symbolsToAnalyze.getNumberBranchesAlwaysTaken( symbolSetName )
570           << " branches always taken" << std::endl << "   "
571           << symbolsToAnalyze.getNumberBranchesNeverTaken( symbolSetName )
572           << " branches never taken" << std::endl << "   "
573           << symbolsToAnalyze.getNumberBranchesNotExecuted( symbolSetName ) * 2
574           << " branch paths not executed" << std::endl
575           << "Percentage branch paths covered  : "
576           << std::fixed << std::setprecision( 2 ) << std::setw( 4 )
577           << 100.0 - percentageBranches << std::endl;
578
579  }
580
581  CloseFile( report );
582}
583
584void GenerateReports(
585  const std::string&              symbolSetName,
586  Coverage::Explanations&         allExplanations,
587  bool                            verbose,
588  const std::string&              projectName,
589  const std::string&              outputDirectory,
590  const Coverage::DesiredSymbols& symbolsToAnalyze,
591  bool                            branchInfoAvailable
592)
593{
594  using reportList_ptr = std::unique_ptr<ReportsBase>;
595  using reportList = std::vector<reportList_ptr>;
596
597  reportList             reports;
598  std::string            reportName;
599  time_t                 timestamp;
600
601
602  timestamp = time( NULL ); /* get current cal time */
603  reports.emplace_back(
604    new ReportsText(
605      timestamp,
606      symbolSetName,
607      allExplanations,
608      projectName,
609      outputDirectory,
610      symbolsToAnalyze,
611      branchInfoAvailable
612    )
613  );
614  reports.emplace_back(
615    new ReportsHtml(
616      timestamp,
617      symbolSetName,
618      allExplanations,
619      projectName,
620      outputDirectory,
621      symbolsToAnalyze,
622      branchInfoAvailable
623    )
624  );
625
626  for ( auto& report: reports ) {
627
628    reportName = "index" + report->ReportExtension();
629    if ( verbose ) {
630      std::cerr << "Generate " << reportName << std::endl;
631    }
632    report->WriteIndex( reportName );
633
634    reportName = "annotated" + report->ReportExtension();
635    if ( verbose ) {
636      std::cerr << "Generate " << reportName << std::endl;
637    }
638    report->WriteAnnotatedReport( reportName );
639
640    reportName = "branch" + report->ReportExtension();
641    if ( verbose ) {
642      std::cerr << "Generate " << reportName << std::endl;
643    }
644    report->WriteBranchReport( reportName );
645
646    reportName = "uncovered" + report->ReportExtension();
647    if ( verbose ) {
648      std::cerr << "Generate " << reportName << std::endl;
649    }
650    report->WriteCoverageReport( reportName );
651
652    reportName = "sizes" + report->ReportExtension();
653    if ( verbose ) {
654      std::cerr << "Generate " << reportName << std::endl;
655    }
656    report->WriteSizeReport( reportName );
657
658    reportName = "symbolSummary" + report->ReportExtension();
659    if ( verbose ) {
660      std::cerr << "Generate " << reportName << std::endl;
661    }
662    report->WriteSymbolSummaryReport( reportName, symbolsToAnalyze );
663  }
664
665  ReportsBase::WriteSummaryReport(
666    "summary.txt",
667    symbolSetName,
668    outputDirectory,
669    symbolsToAnalyze,
670    branchInfoAvailable
671  );
672}
673
674}
Note: See TracBrowser for help on using the repository browser.