source: rtems-tools/tester/covoar/covoar.cc @ 6a4859e

Last change on this file since 6a4859e was 6a4859e, checked in by Cillian O'Donnell <cpodonnell8@…>, on Aug 26, 2017 at 8:15:56 AM

covoar: Use rld tempfile and add signals to clean up in event of crash.

Use rld tempfile for temporary files and add fatal signal handling to clean
them up in the event of a crash.

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