source: rtems-tools/tester/covoar/covmerge.cc @ 3e187ba

5
Last change on this file since 3e187ba was 100f517, checked in by Chris Johns <chrisj@…>, on 05/09/14 at 11:50:37

covoar: Merger the covoar source from rtems-testing.git.

Use waf to build covoar.

  • Property mode set to 100644
File size: 12.9 KB
Line 
1#include <stdio.h>
2#include <string.h>
3#include <limits.h>
4#include <ctype.h>
5#include <sys/types.h>
6#include <sys/stat.h>
7#include <fcntl.h>
8#include <netdb.h>
9#include <stdlib.h>
10#include <signal.h>
11#include <unistd.h>
12#include <errno.h>
13
14#include "app_common.h"
15#include "CoverageFactory.h"
16#include "CoverageMap.h"
17#include "CoverageRanges.h"
18#include "Explanations.h"
19#include "ObjdumpProcessor.h"
20#include "Reports.h"
21
22/*
23 *  Variables to control global behavior
24 */
25int verbose = 0;
26Coverage::CoverageFormats_t coverageFormat;
27char *mergedCoverageFile = NULL;
28char *branchReportFile = NULL;
29char *coverageReportFile = NULL;
30char *sizeReportFile = NULL;
31uint32_t lowAddress  = 0xffffffff;
32uint32_t highAddress = 0xffffffff;
33
34char *target = NULL;
35char *executable = NULL;
36char *explanations = NULL;
37char *noExplanations = NULL;
38char *progname;
39
40/*
41 *  Global variables for the program
42 */
43Coverage::CoverageMapBase    *CoverageMap      = NULL;
44Coverage::CoverageReaderBase *CoverageReader   = NULL;
45Coverage::CoverageWriterBase *CoverageWriter   = NULL;
46Coverage::ObjdumpProcessor   *ObjdumpProcessor = NULL;
47Coverage::CoverageRanges     *Ranges           = NULL;
48Coverage::Explanations       *Explanations     = NULL;
49
50int  BranchesAlwaysTaken = 0;
51bool BranchesFound = false;
52int  BranchesNeverTaken = 0;
53int  UncoveredRanges = 0;
54
55/*
56 *  Set of addresses we need source line number for
57 */
58std::list<uint32_t> AddressesNeedingSourceLine;
59
60/*
61 *  Convert string to int with status out
62 */
63
64bool stringToUint32(
65  const char  *s,
66  int          base,
67  uint32_t    *n
68)
69{
70  long long result;
71
72  if ( !n )
73    return false;
74
75  errno = 0;
76  *n    = 0;
77
78  result = strtoll( s, NULL, base );
79
80  if ( (result == 0) && errno )
81    return false;
82
83  if ( (result == LLONG_MAX) && (errno == ERANGE))
84    return false;
85
86  if ( (result == LLONG_MIN) && (errno == ERANGE))
87    return false;
88
89  *n = (uint32_t)result;
90  return true;
91}
92
93/*
94 *  Print program usage message
95 */
96void usage()
97{
98  fprintf(
99    stderr,
100    "Usage: %s [-v] [-t] [-m file] -T TARGET [-e EXECUTABLE]-l ADDRESS -h ADDRESS coverage1... coverageN\n"
101    "\n"
102    "  -l low address   - low address of range to merge\n"
103    "  -l high address  - high address of range to merge\n"
104    "  -f format        - coverage files are in <format> "
105                     "(RTEMS, TSIM or Skyeye)\n"
106    "  -m FILE          - optional merged coverage file to write\n"
107    "  -r REPORT        - optional coverage report to write\n"
108    "  -s REPORT        - optional size report to write\n"
109    "  -T TARGET        - target name\n"
110    "  -e EXECUTABLE    - name of executable to get symbols from\n"
111    "  -E EXPLANATIONS  - name of file with explanations\n"
112    "  -v               - verbose at initialization\n"
113    "\n",
114    progname
115  );
116}
117
118/*
119 *  Look over the coverage map and compute uncovered ranges and branches
120 */
121void ComputeUncovered(void)
122{
123  uint32_t a, la, ha;
124  std::list<Coverage::CoverageRange>::iterator it;
125
126  a = lowAddress;
127  while (a < highAddress) {
128
129    /*
130     *  Find all the unexecuted addresses and add them to the range.
131     */
132    if (!CoverageMap->wasExecuted( a )) {
133      la = a;
134      for (ha=a+1; ha<=highAddress && !CoverageMap->wasExecuted(ha); ha++)
135        ;
136      ha--;
137
138      UncoveredRanges++;
139      Ranges->add( la, ha );
140      AddressesNeedingSourceLine.push_back( la );
141      AddressesNeedingSourceLine.push_back( ha );
142      a = ha + 1;
143    }
144
145    else if (CoverageMap->isBranch( a )) {
146      BranchesFound = true;
147      la = a;
148      for (ha=a+1;
149           ha<=highAddress && !CoverageMap->isStartOfInstruction(ha);
150           ha++)
151        ;
152      ha--;
153
154      if (CoverageMap->wasAlwaysTaken( la )) {
155        BranchesAlwaysTaken++;
156        AddressesNeedingSourceLine.push_back( la );
157      }
158      else if (CoverageMap->wasNeverTaken( la )) {
159        BranchesNeverTaken++;
160        AddressesNeedingSourceLine.push_back( la );
161      }
162      a = ha + 1;
163    }
164    else
165      a++;
166  }
167}
168
169/*
170 *  Find source lines for addresses
171 */
172void FindSourceForAddresses(void)
173{
174  FILE                          *tmpfile;
175  std::list<uint32_t>::iterator  it;
176
177  /*
178   *  Write a temporary file with ranges
179   */
180  if ( verbose )
181    fprintf( stderr, "Writing ranges.tmp input to addr2line\n" );
182
183  tmpfile = fopen( "ranges.tmp", "w" );
184  if ( !tmpfile ) {
185    fprintf( stderr, "Unable to open %s\n\n", "ranges.tmp" );
186    exit(-1);
187  }
188
189  for (it =  AddressesNeedingSourceLine.begin() ;
190       it != AddressesNeedingSourceLine.end() ;
191       it++ ) {
192    fprintf(tmpfile, "0x%08x\n", *it);
193  }
194
195  fclose( tmpfile );
196
197  /*
198   *  Generate a file with the addr2line mapping
199   */
200  if ( verbose )
201    fprintf( stderr, "Running addr2line\n" );
202
203  {
204    char command[512];
205    sprintf(
206      command,
207      "%s -e %s <%s | dos2unix >%s",
208      Tools->getAddr2line(),
209      executable,
210      "ranges.tmp",
211      "ranges01.tmp"
212    );
213    if ( system( command ) ) {
214      fprintf( stderr, "addr2line command (%s) failed\n", command );
215      exit( -1 );
216    }
217  }
218
219  /*
220   *  Go back over the ranges, read the addr2line output, and correlate it.
221   */
222  if ( verbose )
223    fprintf( stderr, "Merging addr2line output into range\n" );
224
225  tmpfile = fopen( "ranges01.tmp", "r" );
226  if ( !tmpfile ) {
227    fprintf( stderr, "Unable to open %s\n\n", "ranges01.tmp" );
228    exit(-1);
229  }
230
231  for (it =  AddressesNeedingSourceLine.begin() ;
232       it != AddressesNeedingSourceLine.end() ;
233       it++ ) {
234    char buffer[512];
235    char *cStatus;
236
237    cStatus = fgets( buffer, 512, tmpfile );
238    if ( cStatus == NULL ) {
239      fprintf( stderr, "Out of sync in addr2line output\n" );
240      exit( -1 );
241    }
242    buffer[ strlen(buffer) - 1] = '\0';
243
244    CoverageMap->setSourceLine( *it, std::string( buffer ) );
245  }
246  fclose( tmpfile );
247}
248
249#define PrintableString(_s) \
250       ((!(_s)) ? "NOT SET" : (_s))
251
252int main(
253  int argc,
254  char **argv
255)
256{
257  int opt;
258  int i;
259  char *format = NULL;
260
261  progname = argv[0];
262
263  while ((opt = getopt(argc, argv, "b:e:E:f:h:l:m:r:s:T:v")) != -1) {
264    switch (opt) {
265      case 'b': branchReportFile   = optarg;  break;
266      case 'e': executable         = optarg;  break;
267      case 'E': explanations       = optarg;  break;
268      case 'm': mergedCoverageFile = optarg;  break;
269      case 'r': coverageReportFile = optarg;  break;
270      case 's': sizeReportFile     = optarg;  break;
271      case 'T': target             = optarg;  break;
272      case 'v': verbose            = 1;       break;
273      case 'f':
274        coverageFormat = Coverage::CoverageFormatToEnum(optarg);
275        format = optarg;
276        break;
277      case 'l':
278        if ( ! stringToUint32( optarg, 16, &lowAddress ) ) {
279          fprintf( stderr, "Low address is not a hexadecimal number\n" );
280          usage();
281          exit(-1);
282        }
283        break;
284      case 'h':
285        if ( ! stringToUint32( optarg, 16, &highAddress ) ) {
286          fprintf( stderr, "High address is not a hexadecimal number\n" );
287          usage();
288          exit(-1);
289        }
290        break;
291      default: /* '?' */
292        usage();
293        exit( -1 );
294    }
295  }
296  if ( verbose ) {
297    fprintf( stderr, "verbose         : %d\n", verbose );
298    fprintf( stderr, "Coverage Format : %s\n", format );
299    fprintf( stderr, "low address     : 0x%08x\n", lowAddress );
300    fprintf( stderr, "high address    : 0x%08x\n", highAddress );
301    fprintf( stderr, "Target          : %s\n", PrintableString(target) );
302    fprintf( stderr, "executable      : %s\n", PrintableString(executable) );
303    fprintf( stderr, "merged coverage : %s\n",
304             PrintableString(mergedCoverageFile) );
305    fprintf( stderr, "\n" );
306  }
307
308  /*
309   * Target name must be set
310   */
311  if ( !target ) {
312    fprintf( stderr, "target must be given.\n\n" );
313    usage();
314    exit(-1);
315  }
316
317  /*
318   *  Validate format
319   */
320  if ( !format ) {
321    fprintf( stderr, "coverage format report must be given.\n\n" );
322    usage();
323    exit(-1);
324  }
325
326  /*
327   * Validate address range
328   */
329  if ( lowAddress == 0xffffffff ) {
330    fprintf( stderr, "Low address not specified.\n\n" );
331    usage();
332    exit(-1);
333  }
334
335  if ( highAddress == 0xffffffff ) {
336    fprintf( stderr, "High address not specified.\n\n" );
337    usage();
338    exit(-1);
339  }
340
341  if ( lowAddress >= highAddress ) {
342    fprintf( stderr, "Low address >= high address.\n\n" );
343    usage();
344    exit(-1);
345  }
346
347  /*
348   *  Create toolnames based on target
349   */
350  TargetInfo = Target::TargetFactory( target );
351
352  /*
353   *  Create a ranges set
354   */
355  Ranges = new Coverage::CoverageRanges();
356  Explanations = new Coverage::Explanations();
357
358  Explanations->load( explanations );
359
360  /*
361   * Create coverage map
362   */
363  CoverageMap = new Coverage::CoverageMap( lowAddress, highAddress );
364  if ( !CoverageMap ) {
365    fprintf( stderr, "Unable to create coverage map.\n\n" );
366    exit(-1);
367  }
368
369  /*
370   * Create input
371   */
372  CoverageReader = Coverage::CreateCoverageReader(coverageFormat);
373  if ( !CoverageReader ) {
374    fprintf( stderr, "Unable to create coverage file reader.\n\n" );
375    exit(-1);
376  }
377
378  /*
379   * Create the objdump processor
380   */
381  ObjdumpProcessor = new Coverage::ObjdumpProcessor();
382
383  /*
384   * Create writer
385   *
386   * NOTE: We ALWAYS write the merged coverage in RTEMS format.
387   */
388  CoverageWriter = Coverage::CreateCoverageWriter(
389    Coverage::COVERAGE_FORMAT_RTEMS
390  );
391  if ( !CoverageWriter ) {
392    fprintf( stderr, "Unable to create coverage file writer.\n\n" );
393    exit(-1);
394  }
395
396  /*
397   * Add in the objdump before reading the coverage information.  We may
398   * want to take advantage of the information line where instructions
399   * begin.
400   */
401  if ( executable ) {
402    if ( verbose )
403      fprintf( stderr, "Reading objdump of %s\n", executable );
404    ObjdumpProcessor->initialize( executable, CoverageMap );
405  }
406
407  /*
408   * Now get to some real work
409   */
410  if ( verbose )
411    fprintf( stderr, "Processing coverage files\n" );
412  for ( i=optind ; i < argc ; i++ ) {
413    //fprintf( stderr, "Processing %s\n", argv[i] );
414    CoverageReader->ProcessFile( argv[i], CoverageMap );
415  }
416
417  /*
418   * Now to write some output
419   */
420  if ( mergedCoverageFile ) {
421    if ( verbose )
422      fprintf(
423        stderr,
424        "Writing merged coverage file (%s)\n",
425        mergedCoverageFile
426      );
427    CoverageWriter->writeFile(
428      mergedCoverageFile,
429      CoverageMap,
430      lowAddress,
431      highAddress
432    );
433  }
434
435  /*
436   *  Marks nops as executed when they are surrounded by executed instructions.
437   */
438  ObjdumpProcessor->markNopsAsExecuted( CoverageMap );
439
440  /*
441   * Iterate over the coverage map and determine the uncovered
442   * ranges and branches.
443   */
444  ComputeUncovered();
445
446  /*
447   *  Look up the source file and line number for the addresses
448   *  of interest.
449   */
450  FindSourceForAddresses();
451
452  /*
453   *  Generate report of ranges not executed
454   */
455  if ( coverageReportFile ) {
456    if ( verbose )
457      fprintf( stderr, "Writing coverage report (%s)\n", coverageReportFile );
458    WriteCoverageReport( coverageReportFile );
459
460    /*
461     *  Let the user know how many cases there were
462     */
463    printf( "%d uncovered ranges found\n", UncoveredRanges );
464  }
465
466  /*
467   *  Generate report of branches taken/not taken
468   */
469  if ( branchReportFile ) {
470    if ( verbose )
471      fprintf( stderr, "Writing branch report (%s)\n", branchReportFile );
472    WriteBranchReport( branchReportFile, lowAddress, highAddress );
473
474    /*
475     *  Let the user know how many branch cases were found
476     */
477    if (!BranchesFound)
478      printf( "No branch information found\n" );
479    else {
480      printf(
481        "%d uncovered branches found\n",
482        BranchesAlwaysTaken + BranchesNeverTaken
483      );
484      printf(
485        "   %d branches always taken\n", BranchesAlwaysTaken
486      );
487      printf(
488        "   %d branches never taken\n", BranchesNeverTaken
489      );
490    }
491  }
492
493  /*
494   *  Simple formatted report of size of ranges
495   */
496  if ( sizeReportFile ) {
497    if ( verbose )
498      fprintf( stderr, "Writing size report (%s)\n", sizeReportFile );
499    WriteSizeReport( sizeReportFile );
500  }
501
502  /*
503   *  Generate annotated assembly file
504   */
505  if ( verbose )
506    fprintf( stderr, "Writing annotated report (%s)\n", "annotated.txt" );
507  WriteAnnotatedReport( "annotated.txt", lowAddress, highAddress );
508
509  /*
510   * write explanations that were not found
511   */
512  std::string str = explanations;
513  str = str + ".NotFound";
514  if ( verbose )
515    fprintf( stderr, "Writing Not Found Report (%s)\n", str.c_str() );
516  Explanations->writeNotFound(str.c_str());
517
518  /*
519   * Calculate coverage percentage
520   */
521  {
522    uint32_t a;
523    uint32_t notExecuted = 0;
524    double   percentage;
525
526    for ( a=lowAddress ; a < highAddress ; a++ ) {
527      if ( !CoverageMap->wasExecuted( a ) )
528        notExecuted++;
529    }
530
531    percentage = (double) notExecuted;
532    percentage /= (double) (highAddress - lowAddress);
533    percentage *= 100.0;
534    printf( "Bytes Analyzed          : %d\n", highAddress - lowAddress );
535    printf( "Bytes Not Executed      : %d\n", notExecuted );
536    printf( "Percentage Executed     : %5.4g\n", 100.0 - percentage  );
537    printf( "Percentage Not Executed : %5.4g\n", percentage  );
538  }
539
540  return 0;
541}
Note: See TracBrowser for help on using the repository browser.