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

5
Last change on this file since e86646c was e86646c, checked in by Joel Sherrill <joel@…>, on 06/22/18 at 15:05:55

covoar: Address kill() on Cygwin

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