source: rtems-tools/tester/covoar/covoar.cc @ cdfc56a

Last change on this file since cdfc56a was cdfc56a, checked in by Chris Johns <chrisj@…>, on May 8, 2018 at 5:09:41 AM

covoar: Control the RLD verbose level from the -v option.

  • 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 (buildPrefix != *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 (buildPath != thisBuildPath) {
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 = Coverage::COVERAGE_FORMAT_QEMU;
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;
231                rld::verbose_inc ();          break;
232      case 'p': projectName         = optarg; break;
233      case 'd': debug               = true;   break;
234      default: /* '?' */
235        usage(progname);
236        exit(EXIT_FAILURE);
237    }
238  }
239  try
240  {
241    /*
242     * Validate inputs.
243     */
244
245    /*
246     * Validate that we have a symbols of interest file.
247     */
248    if ( symbolSet.empty() ) {
249      option = "symbol set file -S";
250      throw option;
251    }
252
253    /*
254     * Has path to explanations.txt been specified.
255     */
256    if ( !explanations ) {
257      option = "explanations -E";
258      throw option;
259    }
260
261    /*
262     * Check for project name.
263     */
264    if ( !projectName ) {
265      option = "project name -p";
266      throw option;
267    }
268  }
269  catch( std::string option )
270  {
271    std::cerr << "error missing option: " + option << std::endl;
272    usage(progname);
273    exit(EXIT_FAILURE);
274  }
275
276  // If a single executable was specified, process the remaining
277  // arguments as coverage file names.
278  if (singleExecutable) {
279
280    // Ensure that the executable is readable.
281    if (!FileIsReadable( singleExecutable )) {
282      std::cerr << "warning: Unable to read executable: " << singleExecutable
283                << std::endl;
284    } else {
285
286      for (int i = optind; i < argc; i++) {
287        // Ensure that the coverage file is readable.
288        if (!FileIsReadable( argv[i] )) {
289          std::cerr << "warning: Unable to read coverage file: " << argv[i]
290                    << std::endl;
291        } else {
292          coverageFileNames.push_back( argv[i] );
293        }
294      }
295
296      // If there was at least one coverage file, create the
297      // executable information.
298      if (!coverageFileNames.empty()) {
299        if (dynamicLibrary) {
300          executableInfo = new Coverage::ExecutableInfo(
301            singleExecutable, dynamicLibrary
302          );
303        } else {
304          executableInfo = new Coverage::ExecutableInfo( singleExecutable );
305        }
306
307        executablesToAnalyze.push_back( executableInfo );
308      }
309    }
310  }
311  else {
312    // If not invoked with a single executable, process the remaining
313    // arguments as executables and derive the coverage file names.
314    for (int i = optind; i < argc; i++) {
315      // Ensure that the executable is readable.
316      if (!FileIsReadable( argv[i] )) {
317        std::cerr << "warning: Unable to read executable: " << argv[i] << std::endl;
318      } else {
319        coverageFileName = argv[i];
320        coverageFileName.append( "." + coverageExtension );
321
322        if (!FileIsReadable( coverageFileName.c_str() )) {
323          std::cerr << "warning: Unable to read coverage file: " << coverageFileName
324                    << std::endl;
325        } else {
326          executableInfo = new Coverage::ExecutableInfo( argv[i] );
327          executablesToAnalyze.push_back( executableInfo );
328          coverageFileNames.push_back( coverageFileName );
329        }
330      }
331    }
332  }
333
334  // Ensure that there is at least one executable to process.
335  if (executablesToAnalyze.empty()) {
336    std::cerr << "error: No information to analyze" << std::endl;
337    exit(EXIT_FAILURE);
338  }
339
340  // The executablesToAnalyze and coverageFileNames containers need
341  // to be the name size of some of the code below breaks. Lets
342  // check and make sure.
343  if (executablesToAnalyze.size() != coverageFileNames.size()) {
344    std::cerr << "ERROR: executables and coverage name size mismatch" << std::endl;
345    exit(EXIT_FAILURE);
346  }
347
348  //
349  // Find the top of the BSP's build tree and if we have found the top
350  // check the executable is under the same path and BSP.
351  //
352  std::string buildPath;
353  std::string buildTarget;
354  std::string buildBSP;
355  createBuildPath(executablesToAnalyze,
356                  buildPath,
357                  buildTarget,
358                  buildBSP);
359
360  //
361  // Use a command line target if provided.
362  //
363  if (!target.empty()) {
364    buildTarget = target;
365  }
366
367  if (Verbose) {
368    if (singleExecutable) {
369      std::cerr << "Processing a single executable and multiple coverage files"
370                << std::endl;
371    } else {
372      std::cerr << "Processing multiple executable/coverage file pairs" << std::endl;
373    }
374    std::cerr << "Coverage Format : " << format << std::endl
375              << "Target          : " << buildTarget.c_str() << std::endl
376              << std::endl;
377
378    // Process each executable/coverage file pair.
379    Executables::iterator eitr = executablesToAnalyze.begin();
380    for (const auto& cname : coverageFileNames) {
381      std::cerr << "Coverage file " << cname
382                << " for executable: " << (*eitr)->getFileName() << std::endl;
383      if (!singleExecutable)
384        eitr++;
385    }
386  }
387
388  //
389  // Create data to support analysis.
390  //
391
392  // Create data based on target.
393  TargetInfo = Target::TargetFactory( buildTarget );
394
395  // Create the set of desired symbols.
396  SymbolsToAnalyze = new Coverage::DesiredSymbols();
397
398  //
399  // Read symbol configuration file and load needed symbols.
400  //
401  if (!SymbolsToAnalyze->load( symbolSet, buildTarget, buildBSP, Verbose )) {
402      exit(EXIT_FAILURE);
403  }
404
405  if ( Verbose )
406    std::cerr << "Analyzing " << SymbolsToAnalyze->set.size()
407              << " symbols" << std::endl;
408
409  // Create explanations.
410  AllExplanations = new Coverage::Explanations();
411  if ( explanations )
412    AllExplanations->load( explanations );
413
414  // Create coverage map reader.
415  coverageReader = Coverage::CreateCoverageReader(coverageFormat);
416  if (!coverageReader) {
417    std::cerr << "error: Unable to create coverage file reader" << std::endl;
418    exit(EXIT_FAILURE);
419  }
420
421  // Create the objdump processor.
422  objdumpProcessor = new Coverage::ObjdumpProcessor();
423
424  // Prepare each executable for analysis.
425  for (auto& exe : executablesToAnalyze) {
426    if (Verbose)
427      std::cerr << "Extracting information from: " << exe->getFileName()
428                << std::endl;
429
430    // If a dynamic library was specified, determine the load address.
431    if (dynamicLibrary) {
432      exe->setLoadAddress( objdumpProcessor->determineLoadAddress( exe ) );
433    }
434
435    // Load the objdump for the symbols in this executable.
436    objdumpProcessor->load( exe, objdumpFile, err );
437  }
438
439  //
440  // Analyze the coverage data.
441  //
442
443  // Process each executable/coverage file pair.
444  Executables::iterator eitr = executablesToAnalyze.begin();
445  for (const auto& cname : coverageFileNames) {
446    Coverage::ExecutableInfo* exe = *eitr;
447    if (Verbose)
448      std::cerr << "Processing coverage file " << cname
449                << " for executable " << exe->getFileName()
450                << std::endl;
451
452    // Process its coverage file.
453    coverageReader->processFile( cname.c_str(), exe );
454
455    // Merge each symbols coverage map into a unified coverage map.
456    exe->mergeCoverage();
457
458    // DEBUG Print ExecutableInfo content
459    //exe->dumpExecutableInfo();
460
461    if (!singleExecutable) {
462      eitr++;
463    }
464  }
465
466  // Do necessary preprocessing of uncovered ranges and branches
467  if (Verbose)
468    std::cerr << "Preprocess uncovered ranges and branches" << std::endl;
469
470  SymbolsToAnalyze->preprocess();
471
472  //
473  // Generate Gcov reports
474  //
475  if (gcnosFileName) {
476    if (Verbose)
477      std::cerr << "Generating Gcov reports..." << std::endl;
478
479    gcnosFile = fopen ( gcnosFileName , "r" );
480
481    if ( !gcnosFile )
482      std::cerr << "Unable to open " << gcnosFileName << std::endl;
483    else {
484      while ( fscanf( gcnosFile, "%s", inputBuffer ) != EOF) {
485        gcovFile = new Gcov::GcovData();
486        strcpy( gcnoFileName, inputBuffer );
487
488        if ( Verbose )
489          std::cerr << "Processing file: " << gcnoFileName << std::endl;
490
491        if ( gcovFile->readGcnoFile( gcnoFileName ) ) {
492          // Those need to be in this order
493          gcovFile->processCounters();
494          gcovFile->writeReportFile();
495          gcovFile->writeGcdaFile();
496          gcovFile->writeGcovFile();
497        }
498
499        delete gcovFile;
500      }
501      fclose( gcnosFile );
502    }
503  }
504
505  // Determine the uncovered ranges and branches.
506  if (Verbose)
507    std::cerr << "Computing uncovered ranges and branches" << std::endl;
508
509  SymbolsToAnalyze->computeUncovered();
510
511  // Calculate remainder of statistics.
512  if (Verbose)
513    std::cerr << "Calculate statistics" << std::endl;
514
515  SymbolsToAnalyze->calculateStatistics();
516
517  // Look up the source lines for any uncovered ranges and branches.
518  if (Verbose)
519    std::cerr << "Looking up source lines for uncovered ranges and branches"
520              << std::endl;
521
522  SymbolsToAnalyze->findSourceForUncovered();
523
524  //
525  // Report the coverage data.
526  //
527  if (Verbose)
528    std::cerr << "Generate Reports" << std::endl;
529
530  Coverage::GenerateReports();
531
532  // Write explanations that were not found.
533  if ( explanations ) {
534    std::string notFound;
535
536    notFound = outputDirectory;
537    notFound += "/";
538    notFound += "ExplanationsNotFound.txt";
539
540    if (Verbose)
541      std::cerr << "Writing Not Found Report (" << notFound<< ')' << std::endl;
542
543    AllExplanations->writeNotFound( notFound.c_str() );
544  }
545
546  //Leave tempfiles around if debug flag (-d) is enabled.
547  if ( debug ) {
548    objdumpFile.override( "objdump_file" );
549    objdumpFile.keep();
550    err.override( "objdump_exec_log" );
551    err.keep();
552    syms.override( "symbols_list" );
553    syms.keep();
554  }
555
556  return 0;
557}
Note: See TracBrowser for help on using the repository browser.