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

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

covoar: Use DWARF to map addresses to source files and lines.

  • Property mode set to 100644
File size: 16.7 KB
RevLine 
[100f517]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
[f381f26]13#include <list>
14
[100f517]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
[6a4859e]26#include "rld-process.h"
27
[f4773ac]28#ifdef _WIN32
29  #define kill(p,s) raise(s)
30#endif
31
[3e187ba]32typedef std::list<std::string> CoverageNames;
33typedef std::list<Coverage::ExecutableInfo*> Executables;
34
[100f517]35/*
[3e187ba]36 * Create a build path from the executable paths. Also extract the build prefix
37 * and BSP names.
[100f517]38 */
[3e187ba]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 {
[70dac81]78          if (buildPrefix != *pri) {
[3e187ba]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 {
[70dac81]100          if (buildPath != thisBuildPath) {
[3e187ba]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}
[100f517]113
114/*
115 *  Print program usage message
116 */
[3e187ba]117void usage(const std::string& progname)
[100f517]118{
[14c7f25]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;
[100f517]139}
140
141#define PrintableString(_s) \
[f9a4b2c]142((!(_s)) ? "NOT SET" : (_s))
[100f517]143
[6a4859e]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
[100f517]178int main(
179  int    argc,
180  char** argv
181)
182{
[3e187ba]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";
[fb987e8]189  Coverage::CoverageFormats_t   coverageFormat = Coverage::COVERAGE_FORMAT_QEMU;
[3e187ba]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;
[6a4859e]210
211  setup_signals();
[100f517]212
213  //
214  // Process command line options.
215  //
[3e187ba]216  progname = rld::path::basename(argv[0]);
[100f517]217
[3e187ba]218  while ((opt = getopt(argc, argv, "1:L:e:c:g:E:f:s:S:T:O:p:vd")) != -1) {
[100f517]219    switch (opt) {
[3e187ba]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;
[100f517]233      default: /* '?' */
[3e187ba]234        usage(progname);
[b516a58]235        exit(EXIT_FAILURE);
[100f517]236    }
237  }
[f9a4b2c]238  try
239  {
[149144f]240    /*
241     * Validate inputs.
242     */
243
244    /*
[3e187ba]245     * Validate that we have a symbols of interest file.
[149144f]246     */
[3e187ba]247    if ( symbolSet.empty() ) {
248      option = "symbol set file -S";
[149144f]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    }
[f9a4b2c]267  }
268  catch( std::string option )
269  {
[3e187ba]270    std::cerr << "error missing option: " + option << std::endl;
271    usage(progname);
[b516a58]272    exit(EXIT_FAILURE);
[f9a4b2c]273  }
[100f517]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 )) {
[14c7f25]281      std::cerr << "warning: Unable to read executable: " << singleExecutable
282                << std::endl;
[149144f]283    } else {
[100f517]284
[3e187ba]285      for (int i = optind; i < argc; i++) {
[100f517]286        // Ensure that the coverage file is readable.
287        if (!FileIsReadable( argv[i] )) {
[14c7f25]288          std::cerr << "warning: Unable to read coverage file: " << argv[i]
289                    << std::endl;
[149144f]290        } else {
[100f517]291          coverageFileNames.push_back( argv[i] );
[149144f]292        }
[100f517]293      }
294
295      // If there was at least one coverage file, create the
296      // executable information.
297      if (!coverageFileNames.empty()) {
[149144f]298        if (dynamicLibrary) {
[100f517]299          executableInfo = new Coverage::ExecutableInfo(
300            singleExecutable, dynamicLibrary
301          );
[149144f]302        } else {
[100f517]303          executableInfo = new Coverage::ExecutableInfo( singleExecutable );
[149144f]304        }
[100f517]305
306        executablesToAnalyze.push_back( executableInfo );
307      }
308    }
309  }
310  else {
[3e187ba]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++) {
[100f517]314      // Ensure that the executable is readable.
315      if (!FileIsReadable( argv[i] )) {
[14c7f25]316        std::cerr << "warning: Unable to read executable: " << argv[i] << std::endl;
[149144f]317      } else {
[100f517]318        coverageFileName = argv[i];
[70dac81]319        coverageFileName.append( "." + coverageExtension );
[100f517]320
321        if (!FileIsReadable( coverageFileName.c_str() )) {
[14c7f25]322          std::cerr << "warning: Unable to read coverage file: " << coverageFileName
323                    << std::endl;
[149144f]324        } else {
[100f517]325          executableInfo = new Coverage::ExecutableInfo( argv[i] );
326          executablesToAnalyze.push_back( executableInfo );
327          coverageFileNames.push_back( coverageFileName );
328        }
329      }
330    }
331  }
332
333  // Ensure that there is at least one executable to process.
334  if (executablesToAnalyze.empty()) {
[14c7f25]335    std::cerr << "error: No information to analyze" << std::endl;
[b516a58]336    exit(EXIT_FAILURE);
[100f517]337  }
338
[3e187ba]339  // The executablesToAnalyze and coverageFileNames containers need
340  // to be the name size of some of the code below breaks. Lets
341  // check and make sure.
342  if (executablesToAnalyze.size() != coverageFileNames.size()) {
343    std::cerr << "ERROR: executables and coverage name size mismatch" << std::endl;
344    exit(EXIT_FAILURE);
345  }
346
347  //
348  // Find the top of the BSP's build tree and if we have found the top
349  // check the executable is under the same path and BSP.
350  //
351  std::string buildPath;
352  std::string buildTarget;
353  std::string buildBSP;
354  createBuildPath(executablesToAnalyze,
355                  buildPath,
356                  buildTarget,
357                  buildBSP);
358
359  //
360  // Use a command line target if provided.
361  //
362  if (!target.empty()) {
363    buildTarget = target;
364  }
365
[100f517]366  if (Verbose) {
[149144f]367    if (singleExecutable) {
[14c7f25]368      std::cerr << "Processing a single executable and multiple coverage files"
369                << std::endl;
[149144f]370    } else {
[14c7f25]371      std::cerr << "Processing multiple executable/coverage file pairs" << std::endl;
[149144f]372    }
[14c7f25]373    std::cerr << "Coverage Format : " << format << std::endl
374              << "Target          : " << buildTarget.c_str() << std::endl
375              << std::endl;
[3e187ba]376
[100f517]377    // Process each executable/coverage file pair.
[3e187ba]378    Executables::iterator eitr = executablesToAnalyze.begin();
[14c7f25]379    for (const auto& cname : coverageFileNames) {
380      std::cerr << "Coverage file " << cname
381                << " for executable: " << (*eitr)->getFileName() << std::endl;
[149144f]382      if (!singleExecutable)
383        eitr++;
[100f517]384    }
385  }
386
387  //
388  // Create data to support analysis.
389  //
390
391  // Create data based on target.
[3e187ba]392  TargetInfo = Target::TargetFactory( buildTarget );
[100f517]393
394  // Create the set of desired symbols.
395  SymbolsToAnalyze = new Coverage::DesiredSymbols();
[3e187ba]396
397  //
398  // Read symbol configuration file and load needed symbols.
399  //
400  if (!SymbolsToAnalyze->load( symbolSet, buildTarget, buildBSP, Verbose )) {
401      exit(EXIT_FAILURE);
[149144f]402  }
[100f517]403
[3e187ba]404  if ( Verbose )
[14c7f25]405    std::cerr << "Analyzing " << SymbolsToAnalyze->set.size()
[3e187ba]406              << " symbols" << std::endl;
407
[100f517]408  // Create explanations.
409  AllExplanations = new Coverage::Explanations();
410  if ( explanations )
411    AllExplanations->load( explanations );
412
413  // Create coverage map reader.
414  coverageReader = Coverage::CreateCoverageReader(coverageFormat);
415  if (!coverageReader) {
[14c7f25]416    std::cerr << "error: Unable to create coverage file reader" << std::endl;
[b516a58]417    exit(EXIT_FAILURE);
[100f517]418  }
419
420  // Create the objdump processor.
421  objdumpProcessor = new Coverage::ObjdumpProcessor();
422
423  // Prepare each executable for analysis.
[14c7f25]424  for (auto& exe : executablesToAnalyze) {
425    if (Verbose)
426      std::cerr << "Extracting information from: " << exe->getFileName()
427                << std::endl;
[100f517]428
429    // If a dynamic library was specified, determine the load address.
[149144f]430    if (dynamicLibrary) {
[14c7f25]431      exe->setLoadAddress( objdumpProcessor->determineLoadAddress( exe ) );
[149144f]432    }
[100f517]433
434    // Load the objdump for the symbols in this executable.
[14c7f25]435    objdumpProcessor->load( exe, objdumpFile, err );
[100f517]436  }
437
438  //
439  // Analyze the coverage data.
440  //
441
442  // Process each executable/coverage file pair.
[3e187ba]443  Executables::iterator eitr = executablesToAnalyze.begin();
444  for (const auto& cname : coverageFileNames) {
[14c7f25]445    Coverage::ExecutableInfo* exe = *eitr;
446    if (Verbose)
447      std::cerr << "Processing coverage file " << cname
448                << " for executable " << exe->getFileName()
449                << std::endl;
[100f517]450
451    // Process its coverage file.
[14c7f25]452    coverageReader->processFile( cname.c_str(), exe );
[100f517]453
454    // Merge each symbols coverage map into a unified coverage map.
[14c7f25]455    exe->mergeCoverage();
[100f517]456
457    // DEBUG Print ExecutableInfo content
[14c7f25]458    //exe->dumpExecutableInfo();
[100f517]459
[149144f]460    if (!singleExecutable) {
[100f517]461      eitr++;
[149144f]462    }
[100f517]463  }
464
465  // Do necessary preprocessing of uncovered ranges and branches
[14c7f25]466  if (Verbose)
467    std::cerr << "Preprocess uncovered ranges and branches" << std::endl;
468
[100f517]469  SymbolsToAnalyze->preprocess();
470
471  //
472  // Generate Gcov reports
473  //
[14c7f25]474  if (gcnosFileName) {
475    if (Verbose)
476      std::cerr << "Generating Gcov reports..." << std::endl;
477
478    gcnosFile = fopen ( gcnosFileName , "r" );
479
480    if ( !gcnosFile )
481      std::cerr << "Unable to open " << gcnosFileName << std::endl;
482    else {
483      while ( fscanf( gcnosFile, "%s", inputBuffer ) != EOF) {
484        gcovFile = new Gcov::GcovData();
485        strcpy( gcnoFileName, inputBuffer );
486
487        if ( Verbose )
488          std::cerr << "Processing file: " << gcnoFileName << std::endl;
489
490        if ( gcovFile->readGcnoFile( gcnoFileName ) ) {
491          // Those need to be in this order
492          gcovFile->processCounters();
493          gcovFile->writeReportFile();
494          gcovFile->writeGcdaFile();
495          gcovFile->writeGcovFile();
496        }
[100f517]497
[14c7f25]498        delete gcovFile;
[100f517]499      }
[14c7f25]500      fclose( gcnosFile );
[100f517]501    }
502  }
503
504  // Determine the uncovered ranges and branches.
[14c7f25]505  if (Verbose)
506    std::cerr << "Computing uncovered ranges and branches" << std::endl;
507
[100f517]508  SymbolsToAnalyze->computeUncovered();
509
510  // Calculate remainder of statistics.
[14c7f25]511  if (Verbose)
512    std::cerr << "Calculate statistics" << std::endl;
513
[100f517]514  SymbolsToAnalyze->calculateStatistics();
515
516  // Look up the source lines for any uncovered ranges and branches.
[14c7f25]517  if (Verbose)
518    std::cerr << "Looking up source lines for uncovered ranges and branches"
519              << std::endl;
520
[100f517]521  SymbolsToAnalyze->findSourceForUncovered();
522
523  //
524  // Report the coverage data.
525  //
[14c7f25]526  if (Verbose)
527    std::cerr << "Generate Reports" << std::endl;
528
[100f517]529  Coverage::GenerateReports();
530
531  // Write explanations that were not found.
532  if ( explanations ) {
533    std::string notFound;
534
535    notFound = outputDirectory;
536    notFound += "/";
537    notFound += "ExplanationsNotFound.txt";
538
[14c7f25]539    if (Verbose)
540      std::cerr << "Writing Not Found Report (" << notFound<< ')' << std::endl;
541
[100f517]542    AllExplanations->writeNotFound( notFound.c_str() );
543  }
544
[6a4859e]545  //Leave tempfiles around if debug flag (-d) is enabled.
546  if ( debug ) {
[149144f]547    objdumpFile.override( "objdump_file" );
548    objdumpFile.keep();
549    err.override( "objdump_exec_log" );
550    err.keep();
[3e187ba]551    syms.override( "symbols_list" );
552    syms.keep();
[6a4859e]553  }
[14c7f25]554
[100f517]555  return 0;
556}
Note: See TracBrowser for help on using the repository browser.