source: rtems-tools/tester/covoar/covoar.cc @ 14c7f25

Last change on this file since 14c7f25 was 14c7f25, checked in by Chris Johns <chrisj@…>, on Apr 28, 2018 at 8:12:46 AM

tester/covoar: Remove the C part of main and convert to C++

This is a clean up.

  • Property mode set to 100644
File size: 16.8 KB
Line 
1#include <ctype.h>
2#include <errno.h>
3#include <fcntl.h>
4#include <limits.h>
5#include <signal.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <sys/types.h>
10#include <sys/stat.h>
11#include <unistd.h>
12
13#include <list>
14
15#include "app_common.h"
16#include "CoverageFactory.h"
17#include "CoverageMap.h"
18#include "DesiredSymbols.h"
19#include "ExecutableInfo.h"
20#include "Explanations.h"
21#include "ObjdumpProcessor.h"
22#include "ReportsBase.h"
23#include "TargetFactory.h"
24#include "GcovData.h"
25
26#include "rld-process.h"
27
28#ifdef _WIN32
29  #define kill(p,s) raise(s)
30#endif
31
32typedef std::list<std::string> CoverageNames;
33typedef std::list<Coverage::ExecutableInfo*> Executables;
34
35/*
36 * Create a build path from the executable paths. Also extract the build prefix
37 * and BSP names.
38 */
39static void createBuildPath(Executables& executablesToAnalyze,
40                            std::string& buildPath,
41                            std::string& buildPrefix,
42                            std::string& buildBSP)
43{
44  for (const auto& exe : executablesToAnalyze) {
45    rld::strings eparts;
46    rld::split(eparts, rld::path::path_abs(exe->getFileName()), RLD_PATH_SEPARATOR);
47    std::string fail; // empty means all is OK else an error string
48    for (rld::path::paths::reverse_iterator pri = eparts.rbegin();
49         pri != eparts.rend();
50         ++pri) {
51      if (*pri == "testsuites") {
52        ++pri;
53        if (pri == eparts.rend()) {
54          fail = "invalid executable path, no BSP";
55          break;
56        }
57        if (buildBSP.empty()) {
58          buildBSP = *pri;
59        } else {
60          if (buildBSP != *pri) {
61            fail = "executable BSP does not match: " + buildBSP;
62            break;
63          }
64        }
65        ++pri;
66        if (pri == eparts.rend() || *pri != "c") {
67          fail = "invalid executable path, no 'c'";
68          break;
69        }
70        ++pri;
71        if (pri == eparts.rend()) {
72          fail = "invalid executable path, no arch prefix";
73          break;
74        }
75        if (buildPrefix.empty()) {
76          buildPrefix = *pri;
77        } else {
78          if (buildBSP != *pri) {
79            fail = "executable build prefix does not match: " + buildPrefix;
80            break;
81          }
82        }
83        ++pri;
84        if (pri == eparts.rend()) {
85          fail = "invalid executable path, no build top";
86          break;
87        }
88        //
89        // The remaining parts of the path is the build path. Iterator over them
90        // and collect into a new paths variable to join to make a path.
91        //
92        rld::path::paths bparts;
93        for (; pri != eparts.rend(); ++pri)
94          bparts.insert(bparts.begin(), *pri);
95        std::string thisBuildPath;
96        rld::path::path_join(thisBuildPath, bparts, thisBuildPath);
97        if (buildPath.empty()) {
98          buildPath = thisBuildPath;
99        } else {
100          if (buildBSP != *pri) {
101            fail = "executable build path does not match: " + buildPath;
102          }
103        }
104        break;
105      }
106    }
107    if (!fail.empty()) {
108      std::cerr << "ERROR: " << fail << std::endl;
109      exit(EXIT_FAILURE);
110    }
111  }
112}
113
114/*
115 *  Print program usage message
116 */
117void usage(const std::string& progname)
118{
119  std::cerr <<"Usage: " << progname
120            <<" [-v] -T TARGET -f FORMAT [-E EXPLANATIONS] -1 EXECUTABLE coverage1 ... coverageN" << std::endl
121            << "--OR--" << std::endl
122            << "Usage: " << progname
123            << " [-v] -T TARGET -f FORMAT [-E EXPLANATIONS] -e EXE_EXTENSION -c COVERAGEFILE_EXTENSION EXECUTABLE1 ... EXECUTABLE2" << std::endl
124            << std::endl
125            << "  -v                        - verbose at initialization" << std::endl
126            << "  -T TARGET                 - target name" << std::endl
127            << "  -f FORMAT                 - coverage file format (RTEMS, QEMU, TSIM or Skyeye)" << std::endl
128            << "  -E EXPLANATIONS           - name of file with explanations" << std::endl
129            << "  -s SYMBOL_SET_FILE        - path to the INI format symbol sets" << std::endl
130            << "  -1 EXECUTABLE             - name of executable to get symbols from" << std::endl
131            << "  -e EXE_EXTENSION          - extension of the executables to analyze" << std::endl
132            << "  -c COVERAGEFILE_EXTENSION - extension of the coverage files to analyze" << std::endl
133            << "  -g GCNOS_LIST             - name of file with list of *.gcno files" << std::endl
134            << "  -p PROJECT_NAME           - name of the project" << std::endl
135            << "  -C ConfigurationFileName  - name of configuration file" << std::endl
136            << "  -O Output_Directory       - name of output directory (default=." << std::endl
137            << "  -d debug                  - disable cleaning of tempfile" << std::endl
138            << std::endl;
139}
140
141#define PrintableString(_s) \
142((!(_s)) ? "NOT SET" : (_s))
143
144static void
145fatal_signal( int signum )
146{
147  signal( signum, SIG_DFL );
148
149  rld::process::temporaries_clean_up();
150
151  /*
152   * Get the same signal again, this time not handled, so its normal effect
153   * occurs.
154   */
155  kill( getpid(), signum );
156}
157
158static void
159setup_signals( void )
160{
161  if ( signal( SIGINT, SIG_IGN ) != SIG_IGN )
162    signal( SIGINT, fatal_signal );
163#ifdef SIGHUP
164  if ( signal( SIGHUP, SIG_IGN ) != SIG_IGN )
165    signal( SIGHUP, fatal_signal );
166#endif
167  if ( signal( SIGTERM, SIG_IGN ) != SIG_IGN )
168    signal( SIGTERM, fatal_signal );
169#ifdef SIGPIPE
170  if ( signal( SIGPIPE, SIG_IGN ) != SIG_IGN )
171    signal( SIGPIPE, fatal_signal );
172#endif
173#ifdef SIGCHLD
174  signal( SIGCHLD, SIG_DFL );
175#endif
176}
177
178int main(
179  int    argc,
180  char** argv
181)
182{
183  CoverageNames                 coverageFileNames;
184  std::string                   coverageFileName;
185  Executables                   executablesToAnalyze;
186  Coverage::ExecutableInfo*     executableInfo = NULL;
187  std::string                   executableExtension = "exe";
188  std::string                   coverageExtension = "cov";
189  Coverage::CoverageFormats_t   coverageFormat;
190  Coverage::CoverageReaderBase* coverageReader = NULL;
191  char*                         executable = NULL;
192  const char*                   explanations = NULL;
193  const char*                   gcnosFileName = NULL;
194  char                          gcnoFileName[FILE_NAME_LENGTH];
195  char                          gcdaFileName[FILE_NAME_LENGTH];
196  char                          gcovBashCommand[256];
197  std::string                   target;
198  const char*                   format = "html";
199  FILE*                         gcnosFile = NULL;
200  Gcov::GcovData*               gcovFile;
201  const char*                   singleExecutable = NULL;
202  rld::process::tempfile        objdumpFile( ".dmp" );
203  rld::process::tempfile        err( ".err" );
204  rld::process::tempfile        syms( ".syms" );
205  bool                          debug = false;
206  std::string                   symbolSet;
207  std::string                   progname;
208  std::string                   option;
209  int                           opt;
210
211  setup_signals();
212
213  //
214  // Process command line options.
215  //
216  progname = rld::path::basename(argv[0]);
217
218  while ((opt = getopt(argc, argv, "1:L:e:c:g:E:f:s:S:T:O:p:vd")) != -1) {
219    switch (opt) {
220      case '1': singleExecutable    = optarg; break;
221      case 'L': dynamicLibrary      = optarg; break;
222      case 'e': executableExtension = optarg; break;
223      case 'c': coverageExtension   = optarg; break;
224      case 'g': gcnosFileName       = optarg; break;
225      case 'E': explanations        = optarg; break;
226      case 'f': format              = optarg; break;
227      case 'S': symbolSet           = optarg; break;
228      case 'T': target              = optarg; break;
229      case 'O': outputDirectory     = optarg; break;
230      case 'v': Verbose             = true;   break;
231      case 'p': projectName         = optarg; break;
232      case 'd': debug               = true;   break;
233      default: /* '?' */
234        usage(progname);
235        exit(EXIT_FAILURE);
236    }
237  }
238  try
239  {
240    /*
241     * Validate inputs.
242     */
243
244    /*
245     * Validate that we have a symbols of interest file.
246     */
247    if ( symbolSet.empty() ) {
248      option = "symbol set file -S";
249      throw option;
250    }
251
252    /*
253     * Has path to explanations.txt been specified.
254     */
255    if ( !explanations ) {
256      option = "explanations -E";
257      throw option;
258    }
259
260    /*
261     * Check for project name.
262     */
263    if ( !projectName ) {
264      option = "project name -p";
265      throw option;
266    }
267  }
268  catch( std::string option )
269  {
270    std::cerr << "error missing option: " + option << std::endl;
271    usage(progname);
272    exit(EXIT_FAILURE);
273  }
274
275  // If a single executable was specified, process the remaining
276  // arguments as coverage file names.
277  if (singleExecutable) {
278
279    // Ensure that the executable is readable.
280    if (!FileIsReadable( singleExecutable )) {
281      std::cerr << "warning: Unable to read executable: " << singleExecutable
282                << std::endl;
283    } else {
284
285      for (int i = optind; i < argc; i++) {
286        // Ensure that the coverage file is readable.
287        if (!FileIsReadable( argv[i] )) {
288          std::cerr << "warning: Unable to read coverage file: " << argv[i]
289                    << std::endl;
290        } else {
291          coverageFileNames.push_back( argv[i] );
292        }
293      }
294
295      // If there was at least one coverage file, create the
296      // executable information.
297      if (!coverageFileNames.empty()) {
298        if (dynamicLibrary) {
299          executableInfo = new Coverage::ExecutableInfo(
300            singleExecutable, dynamicLibrary
301          );
302        } else {
303          executableInfo = new Coverage::ExecutableInfo( singleExecutable );
304        }
305
306        executablesToAnalyze.push_back( executableInfo );
307      }
308    }
309  }
310  else {
311    // If not invoked with a single executable, process the remaining
312    // arguments as executables and derive the coverage file names.
313    for (int i = optind; i < argc; i++) {
314      // Ensure that the executable is readable.
315      if (!FileIsReadable( argv[i] )) {
316        std::cerr << "warning: Unable to read executable: " << argv[i] << std::endl;
317      } else {
318        coverageFileName = argv[i];
319        coverageFileName.replace(
320          coverageFileName.length() - executableExtension.size(),
321          executableExtension.size(),
322          coverageExtension
323        );
324
325        if (!FileIsReadable( coverageFileName.c_str() )) {
326          std::cerr << "warning: Unable to read coverage file: " << coverageFileName
327                    << std::endl;
328        } else {
329          executableInfo = new Coverage::ExecutableInfo( argv[i] );
330          executablesToAnalyze.push_back( executableInfo );
331          coverageFileNames.push_back( coverageFileName );
332        }
333      }
334    }
335  }
336
337  // Ensure that there is at least one executable to process.
338  if (executablesToAnalyze.empty()) {
339    std::cerr << "error: No information to analyze" << std::endl;
340    exit(EXIT_FAILURE);
341  }
342
343  // The executablesToAnalyze and coverageFileNames containers need
344  // to be the name size of some of the code below breaks. Lets
345  // check and make sure.
346  if (executablesToAnalyze.size() != coverageFileNames.size()) {
347    std::cerr << "ERROR: executables and coverage name size mismatch" << std::endl;
348    exit(EXIT_FAILURE);
349  }
350
351  //
352  // Find the top of the BSP's build tree and if we have found the top
353  // check the executable is under the same path and BSP.
354  //
355  std::string buildPath;
356  std::string buildTarget;
357  std::string buildBSP;
358  createBuildPath(executablesToAnalyze,
359                  buildPath,
360                  buildTarget,
361                  buildBSP);
362
363  //
364  // Use a command line target if provided.
365  //
366  if (!target.empty()) {
367    buildTarget = target;
368  }
369
370  if (Verbose) {
371    if (singleExecutable) {
372      std::cerr << "Processing a single executable and multiple coverage files"
373                << std::endl;
374    } else {
375      std::cerr << "Processing multiple executable/coverage file pairs" << std::endl;
376    }
377    std::cerr << "Coverage Format : " << format << std::endl
378              << "Target          : " << buildTarget.c_str() << std::endl
379              << std::endl;
380
381    // Process each executable/coverage file pair.
382    Executables::iterator eitr = executablesToAnalyze.begin();
383    for (const auto& cname : coverageFileNames) {
384      std::cerr << "Coverage file " << cname
385                << " for executable: " << (*eitr)->getFileName() << std::endl;
386      if (!singleExecutable)
387        eitr++;
388    }
389  }
390
391  //
392  // Create data to support analysis.
393  //
394
395  // Create data based on target.
396  TargetInfo = Target::TargetFactory( buildTarget );
397
398  // Create the set of desired symbols.
399  SymbolsToAnalyze = new Coverage::DesiredSymbols();
400
401  //
402  // Read symbol configuration file and load needed symbols.
403  //
404  if (!SymbolsToAnalyze->load( symbolSet, buildTarget, buildBSP, Verbose )) {
405      exit(EXIT_FAILURE);
406  }
407
408  if ( Verbose )
409    std::cerr << "Analyzing " << SymbolsToAnalyze->set.size()
410              << " symbols" << std::endl;
411
412  // Create explanations.
413  AllExplanations = new Coverage::Explanations();
414  if ( explanations )
415    AllExplanations->load( explanations );
416
417  // Create coverage map reader.
418  coverageReader = Coverage::CreateCoverageReader(coverageFormat);
419  if (!coverageReader) {
420    std::cerr << "error: Unable to create coverage file reader" << std::endl;
421    exit(EXIT_FAILURE);
422  }
423
424  // Create the objdump processor.
425  objdumpProcessor = new Coverage::ObjdumpProcessor();
426
427  // Prepare each executable for analysis.
428  for (auto& exe : executablesToAnalyze) {
429    if (Verbose)
430      std::cerr << "Extracting information from: " << exe->getFileName()
431                << std::endl;
432
433    // If a dynamic library was specified, determine the load address.
434    if (dynamicLibrary) {
435      exe->setLoadAddress( objdumpProcessor->determineLoadAddress( exe ) );
436    }
437
438    // Load the objdump for the symbols in this executable.
439    objdumpProcessor->load( exe, objdumpFile, err );
440  }
441
442  //
443  // Analyze the coverage data.
444  //
445
446  // Process each executable/coverage file pair.
447  Executables::iterator eitr = executablesToAnalyze.begin();
448  for (const auto& cname : coverageFileNames) {
449    Coverage::ExecutableInfo* exe = *eitr;
450    if (Verbose)
451      std::cerr << "Processing coverage file " << cname
452                << " for executable " << exe->getFileName()
453                << std::endl;
454
455    // Process its coverage file.
456    coverageReader->processFile( cname.c_str(), exe );
457
458    // Merge each symbols coverage map into a unified coverage map.
459    exe->mergeCoverage();
460
461    // DEBUG Print ExecutableInfo content
462    //exe->dumpExecutableInfo();
463
464    if (!singleExecutable) {
465      eitr++;
466    }
467  }
468
469  // Do necessary preprocessing of uncovered ranges and branches
470  if (Verbose)
471    std::cerr << "Preprocess uncovered ranges and branches" << std::endl;
472
473  SymbolsToAnalyze->preprocess();
474
475  //
476  // Generate Gcov reports
477  //
478  if (gcnosFileName) {
479    if (Verbose)
480      std::cerr << "Generating Gcov reports..." << std::endl;
481
482    gcnosFile = fopen ( gcnosFileName , "r" );
483
484    if ( !gcnosFile )
485      std::cerr << "Unable to open " << gcnosFileName << std::endl;
486    else {
487      while ( fscanf( gcnosFile, "%s", inputBuffer ) != EOF) {
488        gcovFile = new Gcov::GcovData();
489        strcpy( gcnoFileName, inputBuffer );
490
491        if ( Verbose )
492          std::cerr << "Processing file: " << gcnoFileName << std::endl;
493
494        if ( gcovFile->readGcnoFile( gcnoFileName ) ) {
495          // Those need to be in this order
496          gcovFile->processCounters();
497          gcovFile->writeReportFile();
498          gcovFile->writeGcdaFile();
499          gcovFile->writeGcovFile();
500        }
501
502        delete gcovFile;
503      }
504      fclose( gcnosFile );
505    }
506  }
507
508  // Determine the uncovered ranges and branches.
509  if (Verbose)
510    std::cerr << "Computing uncovered ranges and branches" << std::endl;
511
512  SymbolsToAnalyze->computeUncovered();
513
514  // Calculate remainder of statistics.
515  if (Verbose)
516    std::cerr << "Calculate statistics" << std::endl;
517
518  SymbolsToAnalyze->calculateStatistics();
519
520  // Look up the source lines for any uncovered ranges and branches.
521  if (Verbose)
522    std::cerr << "Looking up source lines for uncovered ranges and branches"
523              << std::endl;
524
525  SymbolsToAnalyze->findSourceForUncovered();
526
527  //
528  // Report the coverage data.
529  //
530  if (Verbose)
531    std::cerr << "Generate Reports" << std::endl;
532
533  Coverage::GenerateReports();
534
535  // Write explanations that were not found.
536  if ( explanations ) {
537    std::string notFound;
538
539    notFound = outputDirectory;
540    notFound += "/";
541    notFound += "ExplanationsNotFound.txt";
542
543    if (Verbose)
544      std::cerr << "Writing Not Found Report (" << notFound<< ')' << std::endl;
545
546    AllExplanations->writeNotFound( notFound.c_str() );
547  }
548
549  //Leave tempfiles around if debug flag (-d) is enabled.
550  if ( debug ) {
551    objdumpFile.override( "objdump_file" );
552    objdumpFile.keep();
553    err.override( "objdump_exec_log" );
554    err.keep();
555    syms.override( "symbols_list" );
556    syms.keep();
557  }
558
559  return 0;
560}
Note: See TracBrowser for help on using the repository browser.