source: rtems-tools/tester/covoar/DesiredSymbols.cc @ 70dac81

5
Last change on this file since 70dac81 was 3e187ba, checked in by Cillian O'Donnell <cpodonnell8@…>, on 04/25/18 at 20:33:58

covoar: Add symbol set reader and ELF data parser to covoar.

Add ability to organize symbol sets of libraries in INI file
and then read them with covoar and load the symbols directly from the
libraries.

rtems-tools/../testing: Add configuration files for coverage analysis.

A number of covoar options are not required and are defaulted.

Co-author: Krzysztof Miesowicz <krzysztof.miesowicz@…>
Co-author: Vijay Kumar Banerjee <vijaykumar9597@…>
Co-author: Chris Johns <chrisj@…>

  • Property mode set to 100644
File size: 20.2 KB
Line 
1/*! @file DesiredSymbols.cc
2 *  @brief DesiredSymbols Implementation
3 *
4 *  This file contains the implementation of the functions
5 *  which provide the functionality of the DesiredSymbols.
6 */
7
8#ifdef __CYGWIN__
9#undef __STRICT_ANSI__
10#endif
11
12#include <libgen.h>
13#include <limits.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <unistd.h>
18
19#include <iostream>
20
21#include "rld.h"
22#include <rld-config.h>
23#include "rld-symbols.h"
24#include "rld-files.h"
25
26#include "DesiredSymbols.h"
27#include "app_common.h"
28#include "CoverageMap.h"
29#include "ObjdumpProcessor.h"
30
31namespace Coverage {
32
33  DesiredSymbols::DesiredSymbols()
34  {
35  }
36
37  DesiredSymbols::~DesiredSymbols()
38  {
39  }
40
41  bool DesiredSymbols::load(
42    const std::string& symbolsSet,
43    const std::string& buildTarget,
44    const std::string& buildBSP,
45    bool               verbose
46  )
47  {
48    rld::files::cache cache;
49    bool              r = true;
50
51    //
52    // Load the INI file looking for a top level:
53    //
54    //  [symbols-sets]
55    //  sets = A, B, C
56    //
57    // For each set read the libraries from the configuration file and load.
58    //
59    //  [A]
60    //  libraries = @BUILD-PREFIX@/c/@BSP@/A/libA.a
61    //
62    //  [B]
63    //  libraries = @BUILD-PREFIX@/c/@BSP@/B/libB.a
64    //
65    try {
66      cache.open();
67
68      rld::config::config config;
69
70      if (verbose)
71        std::cerr << "Loading symbol sets: " << symbolsSet << std::endl;
72
73      config.load (symbolsSet);
74
75      const rld::config::section& sym_section = config.get_section("symbol-sets");
76
77      rld::strings sets;
78      rld::config::parse_items (sym_section, "sets", sets, true);
79
80      for (const std::string set : sets) {
81        if (verbose)
82          std::cerr << " Symbol set: " << set << std::endl;
83        const rld::config::section& set_section = config.get_section(set);
84        rld::strings libs;
85        rld::config::parse_items (set_section, "libraries", libs, true);
86        for (std::string lib : libs) {
87          lib = rld::find_replace(lib, "@BUILD-TARGET@", buildTarget);
88          lib = rld::find_replace(lib, "@BSP@", buildBSP);
89          if (verbose)
90            std::cerr << " Loading library: " << lib << std::endl;
91          cache.add(lib);
92        }
93      }
94
95      rld::symbols::table symbols;
96
97      cache.load_symbols (symbols, true);
98
99      for (auto& kv : symbols.globals()) {
100        const rld::symbols::symbol& sym = *(kv.second);
101        set[sym.name()] = *(new SymbolInformation);
102      }
103      for (auto& kv : symbols.weaks()) {
104        const rld::symbols::symbol& sym = *(kv.second);
105        set[sym.name()] = *(new SymbolInformation);
106      }
107      for (auto& kv : symbols.locals()) {
108        const rld::symbols::symbol& sym = *(kv.second);
109        set[sym.name()] = *(new SymbolInformation);
110      }
111
112    } catch (rld::error re) {
113      std::cerr << "error: "
114                << re.where << ": " << re.what
115                << std::endl;
116      r = false;
117    } catch (...) {
118      cache.close();
119      throw;
120    }
121
122    cache.close();
123
124    return r;
125  }
126
127  void DesiredSymbols::preprocess( void )
128  {
129    ObjdumpProcessor::objdumpLines_t::iterator fitr;
130    ObjdumpProcessor::objdumpLines_t::iterator n, p;
131    DesiredSymbols::symbolSet_t::iterator      sitr;
132    CoverageMapBase*                           theCoverageMap;
133
134    // Look at each symbol.
135    for (sitr = SymbolsToAnalyze->set.begin();
136         sitr != SymbolsToAnalyze->set.end();
137         sitr++) {
138
139      // If the unified coverage map does not exist, the symbol was
140      // never referenced by any executable.  Just skip it.
141      theCoverageMap = sitr->second.unifiedCoverageMap;
142      if (!theCoverageMap)
143        continue;
144
145      // Mark any branch and NOP instructions.
146      for (fitr = sitr->second.instructions.begin();
147           fitr != sitr->second.instructions.end();
148           fitr++) {
149        if (fitr->isBranch) {
150           theCoverageMap->setIsBranch(
151             fitr->address - sitr->second.baseAddress
152           );
153        }
154        if (fitr->isNop) {
155           theCoverageMap->setIsNop(
156             fitr->address - sitr->second.baseAddress
157           );
158        }
159      }
160
161    }
162  }
163
164  void DesiredSymbols::calculateStatistics( void )
165  {
166    uint32_t                              a;
167    uint32_t                              endAddress;
168    DesiredSymbols::symbolSet_t::iterator sitr;
169    CoverageMapBase*                      theCoverageMap;
170
171    // Look at each symbol.
172    for (sitr = SymbolsToAnalyze->set.begin();
173         sitr != SymbolsToAnalyze->set.end();
174         sitr++) {
175
176      // If the unified coverage map does not exist, the symbol was
177      // never referenced by any executable.  Just skip it.
178      theCoverageMap = sitr->second.unifiedCoverageMap;
179      if (!theCoverageMap)
180        continue;
181
182      // Increment the total sizeInBytes byt the bytes in the symbol
183      stats.sizeInBytes += sitr->second.stats.sizeInBytes;
184
185      // Now scan through the coverage map of this symbol.
186      endAddress = sitr->second.stats.sizeInBytes - 1;
187      a = 0;
188      while (a <= endAddress) {
189
190        // If we are at the start of instruction increment
191        // instruction type counters as needed.
192        if ( theCoverageMap->isStartOfInstruction( a ) ) {
193
194          stats.sizeInInstructions++;
195          sitr->second.stats.sizeInInstructions++;
196
197          if (!theCoverageMap->wasExecuted( a ) ) {
198            stats.uncoveredInstructions++;
199            sitr->second.stats.uncoveredInstructions++;
200
201            if ( theCoverageMap->isBranch( a )) {
202              stats.branchesNotExecuted++;
203              sitr->second.stats.branchesNotExecuted++;
204             }
205          } else if (theCoverageMap->isBranch( a )) {
206            stats.branchesExecuted++;
207            sitr->second.stats.branchesExecuted++;
208          }
209
210        }
211
212        if (!theCoverageMap->wasExecuted( a )) {
213          stats.uncoveredBytes++;
214          sitr->second.stats.uncoveredBytes++;
215        }
216        a++;
217
218      }
219    }
220  }
221
222
223  void DesiredSymbols::computeUncovered( void )
224  {
225    uint32_t                              a, la, ha;
226    uint32_t                              endAddress;
227    uint32_t                              count;
228    DesiredSymbols::symbolSet_t::iterator sitr;
229    CoverageRanges*                       theBranches;
230    CoverageMapBase*                      theCoverageMap;
231    CoverageRanges*                       theRanges;
232
233    // Look at each symbol.
234    for (sitr = SymbolsToAnalyze->set.begin();
235         sitr != SymbolsToAnalyze->set.end();
236         sitr++) {
237
238      // If the unified coverage map does not exist, the symbol was
239      // never referenced by any executable.  Just skip it.
240      theCoverageMap = sitr->second.unifiedCoverageMap;
241      if (!theCoverageMap)
242        continue;
243
244      // Create containers for the symbol's uncovered ranges and branches.
245      theRanges = new CoverageRanges();
246      sitr->second.uncoveredRanges = theRanges;
247      theBranches = new CoverageRanges();
248      sitr->second.uncoveredBranches = theBranches;
249
250      // Mark NOPs as executed
251      endAddress = sitr->second.stats.sizeInBytes - 1;
252      a = 0;
253      while (a < endAddress) {
254        if (!theCoverageMap->wasExecuted( a )) {
255          a++;
256          continue;
257        }
258
259        for (ha=a+1;
260             ha<=endAddress && !theCoverageMap->isStartOfInstruction( ha );
261             ha++)
262          ;
263        if ( ha >= endAddress )
264          break;
265
266        if (theCoverageMap->isNop( ha ))
267          do {
268            theCoverageMap->setWasExecuted( ha );
269            ha++;
270            if ( ha >= endAddress )
271              break;
272          } while ( !theCoverageMap->isStartOfInstruction( ha ) );
273        a = ha;
274      }
275
276      // Now scan through the coverage map of this symbol.
277      endAddress = sitr->second.stats.sizeInBytes - 1;
278      a = 0;
279      while (a <= endAddress) {
280
281        // If an address was NOT executed, find consecutive unexecuted
282        // addresses and add them to the uncovered ranges.
283        if (!theCoverageMap->wasExecuted( a )) {
284
285          la = a;
286          count = 1;
287          for (ha=a+1;
288               ha<=endAddress && !theCoverageMap->wasExecuted( ha );
289               ha++)
290          {
291            if ( theCoverageMap->isStartOfInstruction( ha ) )
292              count++;
293          }
294          ha--;
295
296          stats.uncoveredRanges++;
297          sitr->second.stats.uncoveredRanges++;
298          theRanges->add(
299            sitr->second.baseAddress + la,
300            sitr->second.baseAddress + ha,
301            CoverageRanges::UNCOVERED_REASON_NOT_EXECUTED,
302            count
303          );
304          a = ha + 1;
305        }
306
307        // If an address is a branch instruction, add any uncovered branches
308        // to the uncoverd branches.
309        else if (theCoverageMap->isBranch( a )) {
310          la = a;
311          for (ha=a+1;
312               ha<=endAddress && !theCoverageMap->isStartOfInstruction( ha );
313               ha++)
314            ;
315          ha--;
316
317          if (theCoverageMap->wasAlwaysTaken( la )) {
318            stats.branchesAlwaysTaken++;
319            sitr->second.stats.branchesAlwaysTaken++;
320            theBranches->add(
321              sitr->second.baseAddress + la,
322              sitr->second.baseAddress + ha,
323              CoverageRanges::UNCOVERED_REASON_BRANCH_ALWAYS_TAKEN,
324              1
325            );
326            if (Verbose)
327              fprintf(
328                stderr,
329                "Branch always taken found in %s (0x%x - 0x%x)\n",
330                (sitr->first).c_str(),
331                sitr->second.baseAddress + la,
332                sitr->second.baseAddress + ha
333              );
334          }
335
336          else if (theCoverageMap->wasNeverTaken( la )) {
337            stats.branchesNeverTaken++;
338            sitr->second.stats.branchesNeverTaken++;
339            theBranches->add(
340              sitr->second.baseAddress + la,
341              sitr->second.baseAddress + ha,
342              CoverageRanges::UNCOVERED_REASON_BRANCH_NEVER_TAKEN,
343              1
344            );
345            if (Verbose)
346              fprintf(
347                stderr,
348                "Branch never taken found in %s (0x%x - 0x%x)\n",
349                (sitr->first).c_str(),
350                sitr->second.baseAddress + la,
351                sitr->second.baseAddress + ha
352              );
353          }
354          a = ha + 1;
355        }
356        else
357          a++;
358      }
359    }
360  }
361
362
363  void DesiredSymbols::createCoverageMap(
364    const std::string& exefileName,
365    const std::string& symbolName,
366    uint32_t           size
367  )
368  {
369    CoverageMapBase*      aCoverageMap;
370    uint32_t              highAddress;
371    symbolSet_t::iterator itr;
372
373    // Ensure that the symbol is a desired symbol.
374    itr = set.find( symbolName );
375
376    if (itr == set.end()) {
377
378      fprintf(
379        stderr,
380        "ERROR: DesiredSymbols::createCoverageMap - Unable to create "
381        "unified coverage map for %s because it is NOT a desired symbol\n",
382        symbolName.c_str()
383      );
384      exit( -1 );
385    }
386
387    // If we have already created a coverage map, ...
388    if (itr->second.unifiedCoverageMap) {
389
390      // ensure that the specified size matches the existing size.
391      if (itr->second.stats.sizeInBytes != size) {
392
393        // Changed ERROR to INFO because size mismatch is not treated as
394        // error anymore.
395        // Set smallest size as size and continue.
396        // Update value for longer byte size.
397        // 2015-07-22
398        fprintf(
399          stderr,
400          "INFO: DesiredSymbols::createCoverageMap - Attempt to create "
401          "unified coverage maps for %s with different sizes (%s/%d != %s/%d)\n",
402
403          symbolName.c_str(),
404          exefileName.c_str(),
405          itr->second.stats.sizeInBytes,
406          itr->second.sourceFile->getFileName().c_str(),
407          size
408        );
409
410        if ( itr->second.stats.sizeInBytes < size )
411          itr->second.stats.sizeInBytes = size;
412        else
413          size = itr->second.stats.sizeInBytes;
414      }
415    }
416
417    // If we don't already have a coverage map, create one.
418    else {
419
420      highAddress = size - 1;
421
422      aCoverageMap = new CoverageMap( exefileName, 0, highAddress );
423      if (!aCoverageMap) {
424
425        fprintf(
426          stderr,
427          "ERROR: DesiredSymbols::createCoverageMap - Unable to allocate "
428          "coverage map for %s:%s\n",
429          exefileName.c_str(),
430          symbolName.c_str()
431        );
432        exit( -1 );
433      }
434
435      if ( Verbose )
436        fprintf(
437          stderr,
438          "Created unified coverage map for %s (0x%x - 0x%x)\n",
439          symbolName.c_str(), 0, highAddress
440        );
441      itr->second.unifiedCoverageMap = aCoverageMap;
442      itr->second.stats.sizeInBytes = size;
443    }
444  }
445
446  void DesiredSymbols::determineSourceLines(
447    CoverageRanges* const theRanges,
448    ExecutableInfo* const theExecutable
449
450  )
451  {
452    char*                              base;
453    char*                              cStatus;
454    char                               command[512];
455    std::string                        fileName;
456    CoverageRanges::ranges_t::iterator ritr;
457    char                               rpath[PATH_MAX];
458    FILE*                              tmpfile;
459
460    // Open a temporary file for the uncovered ranges.
461    tmpfile = fopen( "ranges1.tmp", "w" );
462    if ( !tmpfile ) {
463      fprintf(
464        stderr,
465        "ERROR: DesiredSymbols::determineSourceLines - "
466        "unable to open %s\n",
467        "ranges1.tmp"
468      );
469      exit(-1);
470    }
471
472    // Write the range addresses to the temporary file.
473    for (ritr =  theRanges->set.begin();
474         ritr != theRanges->set.end();
475         ritr++ ) {
476      fprintf(
477        tmpfile,
478        "0x%08x\n0x%08x\n",
479        ritr->lowAddress - theExecutable->getLoadAddress(),
480        ritr->highAddress - theExecutable->getLoadAddress()
481      );
482    }
483
484    fclose( tmpfile );
485
486    // Invoke addr2line to generate the source lines for each address.
487    if (theExecutable->hasDynamicLibrary())
488      fileName = theExecutable->getLibraryName();
489    else
490      fileName = theExecutable->getFileName();
491
492    sprintf(
493      command,
494      "%s -Ce %s <%s | dos2unix >%s",
495      TargetInfo->getAddr2line(),
496      fileName.c_str(),
497      "ranges1.tmp",
498      "ranges2.tmp"
499    );
500
501    if (system( command )) {
502      fprintf(
503        stderr,
504        "ERROR: DesiredSymbols::determineSourceLines - "
505        "command (%s) failed\n",
506        command
507      );
508      exit( -1 );
509    }
510
511    // Open the addr2line output file.
512    tmpfile = fopen( "ranges2.tmp", "r" );
513    if ( !tmpfile ) {
514      fprintf(
515        stderr,
516        "ERROR: DesiredSymbols::determineSourceLines - "
517        "unable to open %s\n",
518        "ranges2.tmp"
519      );
520      exit(-1);
521    }
522
523    // Process the addr2line output.
524    for (ritr =  theRanges->set.begin();
525         ritr != theRanges->set.end();
526         ritr++ ) {
527
528      cStatus = fgets( inputBuffer, MAX_LINE_LENGTH, tmpfile );
529      if ( cStatus == NULL ) {
530        fprintf(
531          stderr,
532          "ERROR: DesiredSymbols::determineSourceLines - "
533          "Out of sync in addr2line output\n"
534        );
535        exit( -1 );
536      }
537      inputBuffer[ strlen(inputBuffer) - 1] = '\0';
538
539      // Use only the base filename without directory path.
540#ifdef _WIN32
541      #define realpath(N,R) _fullpath((R),(N),_MAX_PATH)
542#endif
543      realpath( inputBuffer, rpath );
544      base = basename( rpath );
545
546      ritr->lowSourceLine = std::string( base );
547
548      cStatus = fgets( inputBuffer, MAX_LINE_LENGTH, tmpfile );
549      if ( cStatus == NULL ) {
550        fprintf(
551          stderr,
552          "ERROR: DesiredSymbols::determineSourceLines - "
553          "Out of sync in addr2line output\n"
554        );
555        exit( -1 );
556      }
557      inputBuffer[ strlen(inputBuffer) - 1] = '\0';
558
559      // Use only the base filename without directory path.
560      realpath( inputBuffer, rpath );
561      base = basename( rpath );
562
563      ritr->highSourceLine = std::string( base );
564    }
565
566    fclose( tmpfile );
567    unlink( "ranges1.tmp" );
568    unlink( "ranges2.tmp" );
569  }
570
571  SymbolInformation* DesiredSymbols::find(
572    const std::string& symbolName
573  )
574  {
575    if (set.find( symbolName ) == set.end())
576      return NULL;
577    else
578      return &set[ symbolName ];
579  }
580
581  void DesiredSymbols::findSourceForUncovered( void )
582  {
583    DesiredSymbols::symbolSet_t::iterator ditr;
584    CoverageRanges*                       theBranches;
585    CoverageRanges*                       theRanges;
586
587    // Process uncovered ranges and/or branches for each symbol.
588    for (ditr = SymbolsToAnalyze->set.begin();
589         ditr != SymbolsToAnalyze->set.end();
590         ditr++) {
591
592      // First the unexecuted ranges, ...
593      theRanges = ditr->second.uncoveredRanges;
594      if (theRanges == NULL)
595        continue;
596
597      if (!theRanges->set.empty()) {
598        if (Verbose)
599          fprintf(
600            stderr,
601            "Looking up source lines for uncovered ranges in %s\n",
602            (ditr->first).c_str()
603          );
604        determineSourceLines(
605          theRanges,
606          ditr->second.sourceFile
607        );
608      }
609
610      // then the uncovered branches.
611      theBranches = ditr->second.uncoveredBranches;
612      if (theBranches == NULL)
613        continue;
614
615      if (!theBranches->set.empty()) {
616        if (Verbose)
617          fprintf(
618            stderr,
619            "Looking up source lines for uncovered branches in %s\n",
620            (ditr->first).c_str()
621          );
622        determineSourceLines(
623          theBranches,
624          ditr->second.sourceFile
625        );
626      }
627    }
628  }
629
630  uint32_t DesiredSymbols::getNumberBranchesAlwaysTaken( void ) const {
631    return stats.branchesAlwaysTaken;
632  };
633
634  uint32_t DesiredSymbols::getNumberBranchesFound( void ) const {
635    return (stats.branchesNotExecuted + stats.branchesExecuted);
636  };
637
638  uint32_t DesiredSymbols::getNumberBranchesNeverTaken( void ) const {
639    return stats.branchesNeverTaken;
640  };
641
642  uint32_t DesiredSymbols::getNumberUncoveredRanges( void ) const {
643    return stats.uncoveredRanges;
644  };
645
646  bool DesiredSymbols::isDesired (
647    const std::string& symbolName
648  ) const
649  {
650    if (set.find( symbolName ) == set.end()) {
651      #if 0
652        fprintf( stderr,
653          "Warning: Unable to find symbol %s\n",
654          symbolName.c_str()
655        );
656      #endif
657      return false;
658    }
659    return true;
660  }
661
662  void DesiredSymbols::mergeCoverageMap(
663    const std::string&           symbolName,
664    const CoverageMapBase* const sourceCoverageMap
665  )
666  {
667    uint32_t              dAddress;
668    CoverageMapBase*      destinationCoverageMap;
669    uint32_t              dMapSize;
670    symbolSet_t::iterator itr;
671    uint32_t              sAddress;
672    uint32_t              sBaseAddress;
673    uint32_t              sMapSize;
674    uint32_t              executionCount;
675
676    // Ensure that the symbol is a desired symbol.
677    itr = set.find( symbolName );
678
679    if (itr == set.end()) {
680
681      fprintf(
682        stderr,
683        "ERROR: DesiredSymbols::mergeCoverageMap - Unable to merge "
684        "coverage map for %s because it is NOT a desired symbol\n",
685        symbolName.c_str()
686      );
687      exit( -1 );
688    }
689
690    // Ensure that the source and destination coverage maps
691    // are the same size.
692    // Changed from ERROR msg to INFO, because size mismatch is not
693    // treated as error anymore. 2015-07-20
694    dMapSize = itr->second.stats.sizeInBytes;
695    sBaseAddress = sourceCoverageMap->getFirstLowAddress();
696    sMapSize = sourceCoverageMap->getSize();
697    if (dMapSize != sMapSize) {
698
699      fprintf(
700        stderr,
701        "INFO: DesiredSymbols::mergeCoverageMap - Unable to merge "
702        "coverage map for %s because the sizes are different\n",
703        symbolName.c_str()
704      );
705      return;
706    }
707
708    // Merge the data for each address.
709    destinationCoverageMap = itr->second.unifiedCoverageMap;
710
711    for (dAddress = 0; dAddress < dMapSize; dAddress++) {
712
713      sAddress = dAddress + sBaseAddress;
714
715      // Merge start of instruction indication.
716      if (sourceCoverageMap->isStartOfInstruction( sAddress ))
717        destinationCoverageMap->setIsStartOfInstruction( dAddress );
718
719      // Merge the execution data.
720      executionCount = sourceCoverageMap->getWasExecuted( sAddress );
721      destinationCoverageMap->sumWasExecuted( dAddress, executionCount );
722
723      // Merge the branch data.
724      executionCount = sourceCoverageMap->getWasTaken( sAddress );
725      destinationCoverageMap->sumWasTaken( dAddress, executionCount );
726
727      executionCount = sourceCoverageMap->getWasNotTaken( sAddress );
728      destinationCoverageMap->sumWasNotTaken( dAddress, executionCount );
729    }
730  }
731
732}
Note: See TracBrowser for help on using the repository browser.