[PATCH v5 1/2] covoar: Fix NOP execution marking

Gedare Bloom gedare at rtems.org
Thu Mar 25 19:38:00 UTC 2021


On Thu, Mar 25, 2021 at 10:05 AM Joel Sherrill <joel at rtems.org> wrote:
>
>
>
> On Thu, Mar 25, 2021 at 10:42 AM Gedare Bloom <gedare at rtems.org> wrote:
>>
>> On Thu, Mar 18, 2021 at 12:05 PM Alex White <alex.white at oarcorp.com> wrote:
>> >
>> > Some NOP instructions were not being marked as executed because they
>> > are located at the end of uncovered ranges. This has been fixed.
>> > ---
>> >  tester/covoar/CoverageMapBase.cc  |  10 +++
>> >  tester/covoar/CoverageMapBase.h   |   4 ++
>> >  tester/covoar/DesiredSymbols.cc   |  38 +++++++++--
>> >  tester/covoar/DesiredSymbols.h    |  11 ++-
>> >  tester/covoar/ExecutableInfo.cc   |   2 +-
>> >  tester/covoar/ExecutableInfo.h    |   4 ++
>> >  tester/covoar/ObjdumpProcessor.cc | 109 +++++++++++++++++++++++-------
>> >  7 files changed, 142 insertions(+), 36 deletions(-)
>> >
>> > diff --git a/tester/covoar/CoverageMapBase.cc b/tester/covoar/CoverageMapBase.cc
>> > index ad0080d..6ca5cf7 100644
>> > --- a/tester/covoar/CoverageMapBase.cc
>> > +++ b/tester/covoar/CoverageMapBase.cc
>> > @@ -142,6 +142,11 @@ namespace Coverage {
>> >      return size;
>> >    }
>> >
>> > +  uint32_t CoverageMapBase::getSizeOfRange( size_t index ) const
>> > +  {
>> > +    return Ranges.at(index).size();
>> > +  }
>> > +
>> >    bool CoverageMapBase::getBeginningOfInstruction(
>> >      uint32_t  address,
>> >      uint32_t* beginning
>> > @@ -178,6 +183,11 @@ namespace Coverage {
>> >      return Ranges.front().lowAddress;
>> >    }
>> >
>> > +  uint32_t CoverageMapBase::getLowAddressOfRange( size_t index ) const
>> > +  {
>> > +    return Ranges.at(index).lowAddress;
>> > +  }
>> > +
>> >    bool CoverageMapBase::getRange( uint32_t address, AddressRange& range ) const
>> >    {
>> >      for ( auto r : Ranges ) {
>> > diff --git a/tester/covoar/CoverageMapBase.h b/tester/covoar/CoverageMapBase.h
>> > index 6ad76d3..a58c696 100644
>> > --- a/tester/covoar/CoverageMapBase.h
>> > +++ b/tester/covoar/CoverageMapBase.h
>> > @@ -156,6 +156,8 @@ namespace Coverage {
>> >       */
>> >      int32_t getFirstLowAddress() const;
>> >
>> > +    uint32_t getLowAddressOfRange( size_t index ) const;
>> > +
>> >      /*!
>> >       *  This method returns true and sets the address range if
>> >       *  the address falls with the bounds of an address range
>> > @@ -177,6 +179,8 @@ namespace Coverage {
>> >       */
>> >      uint32_t getSize() const;
>> >
>> > +    uint32_t getSizeOfRange( size_t index ) const;
>> > +
>> >      /*!
>> >       *  This method returns the address of the beginning of the
>> >       *  instruction that contains the specified address.
>> > diff --git a/tester/covoar/DesiredSymbols.cc b/tester/covoar/DesiredSymbols.cc
>> > index b9a5bb7..c97b25c 100644
>> > --- a/tester/covoar/DesiredSymbols.cc
>> > +++ b/tester/covoar/DesiredSymbols.cc
>> > @@ -142,7 +142,7 @@ namespace Coverage {
>> >        CoverageMapBase* theCoverageMap = s.second.unifiedCoverageMap;
>> >        if (theCoverageMap)
>> >        {
>> > -        // Increment the total sizeInBytes byt the bytes in the symbol
>> > +        // Increment the total sizeInBytes by the bytes in the symbol
>> >          stats.sizeInBytes += s.second.stats.sizeInBytes;
>> >
>> >          // Now scan through the coverage map of this symbol.
>> > @@ -202,6 +202,26 @@ namespace Coverage {
>> >          uint32_t count;
>> >
>> >          // Mark NOPs as executed
>> > +        a = s.second.stats.sizeInBytes - 1;
>> > +        count = 0;
>> > +        while (a > 0) {
>> > +          if (theCoverageMap->isStartOfInstruction( a )) {
>> > +            break;
>> > +          }
>> > +
>> > +          count++;
>> > +
>> > +          if (theCoverageMap->isNop( a )) {
>> > +            for (la = a; la < (a + count); la++) {
>> > +              theCoverageMap->setWasExecuted( la );
>> > +            }
>> > +
>> > +            count = 0;
>> > +          }
>> > +
>> > +          a--;
>> > +        }
>> > +
>> >          endAddress = s.second.stats.sizeInBytes - 1;
>> >          a = 0;
>> >          while (a < endAddress) {
>> > @@ -223,12 +243,13 @@ namespace Coverage {
>> >                ha++;
>> >                if ( ha >= endAddress )
>> >                  break;
>> > -            } while ( !theCoverageMap->isStartOfInstruction( ha ) );
>> > +            } while ( !theCoverageMap->isStartOfInstruction( ha ) ||
>> > +                      theCoverageMap->isNop( ha ) );
>> >            a = ha;
>> >          }
>> >
>> >          // Now scan through the coverage map of this symbol.
>> > -        endAddress = s.second.stats.sizeInBytes - 1;
>> > +        endAddress = s.second.stats.sizeInBytesWithoutNops - 1;
>> >          a = 0;
>> >          while (a <= endAddress) {
>> >            // If an address was NOT executed, find consecutive unexecuted
>> > @@ -316,7 +337,8 @@ namespace Coverage {
>> >    void DesiredSymbols::createCoverageMap(
>> >      const std::string& exefileName,
>> >      const std::string& symbolName,
>> > -    uint32_t           size
>> > +    uint32_t           size,
>> > +    uint32_t           sizeWithoutNops
>> >    )
>> >    {
>> >      CoverageMapBase* aCoverageMap;
>> > @@ -354,9 +376,10 @@ namespace Coverage {
>> >                    << '/' << size << ')'
>> >                    << std::endl;
>> >
>> > -        if ( itr->second.stats.sizeInBytes < size )
>> > +        if ( itr->second.stats.sizeInBytes < size ) {
>> >            itr->second.stats.sizeInBytes = size;
>> > -        else
>> > +          itr->second.stats.sizeInBytesWithoutNops = sizeWithoutNops;
>> > +        } else
>> >            size = itr->second.stats.sizeInBytes;
>> >        }
>> >      }
>> > @@ -376,6 +399,7 @@ namespace Coverage {
>> >          );
>> >        itr->second.unifiedCoverageMap = aCoverageMap;
>> >        itr->second.stats.sizeInBytes = size;
>> > +      itr->second.stats.sizeInBytesWithoutNops = sizeWithoutNops;
>> >      }
>> >    }
>> >
>> > @@ -479,7 +503,7 @@ namespace Coverage {
>> >      // are the same size.
>> >      // Changed from ERROR msg to INFO, because size mismatch is not
>> >      // treated as error anymore. 2015-07-20
>> > -    uint32_t dMapSize = sinfo.stats.sizeInBytes;
>> > +    uint32_t dMapSize = sinfo.stats.sizeInBytesWithoutNops;
>> >      uint32_t sBaseAddress = sourceCoverageMap->getFirstLowAddress();
>> >      uint32_t sMapSize = sourceCoverageMap->getSize();
>> >      if (dMapSize != 0 && dMapSize != sMapSize) {
>> > diff --git a/tester/covoar/DesiredSymbols.h b/tester/covoar/DesiredSymbols.h
>> > index 5c45af8..367ca95 100644
>> > --- a/tester/covoar/DesiredSymbols.h
>> > +++ b/tester/covoar/DesiredSymbols.h
>> > @@ -57,7 +57,12 @@ namespace Coverage {
>> >      uint32_t sizeInBytes;
>> >
>> >      /*!
>> > -     *  This member contains the size in Bytes.
>> > +     *  This member contains the size in Bytes not accounting for NOPs.
>> > +     */
>> > +    uint32_t sizeInBytesWithoutNops;
>> > +
>> > +    /*!
>> > +     *  This member contains the size in instructions.
>> >       */
>> >      uint32_t sizeInInstructions;
>> >
>> > @@ -100,6 +105,7 @@ namespace Coverage {
>> >         branchesNeverTaken(0),
>> >         branchesNotExecuted(0),
>> >         sizeInBytes(0),
>> > +       sizeInBytesWithoutNops(0),
>> >         sizeInInstructions(0),
>> >         uncoveredBytes(0),
>> >         uncoveredInstructions(0),
>> > @@ -227,7 +233,8 @@ namespace Coverage {
>> >      void createCoverageMap(
>> >        const std::string& exefileName,
>> >        const std::string& symbolName,
>> > -      uint32_t           size
>> > +      uint32_t           size,
>> > +      uint32_t           sizeWithoutNops
>> >      );
>> >
>> >      /*!
>> > diff --git a/tester/covoar/ExecutableInfo.cc b/tester/covoar/ExecutableInfo.cc
>> > index c593e1d..ddd2987 100644
>> > --- a/tester/covoar/ExecutableInfo.cc
>> > +++ b/tester/covoar/ExecutableInfo.cc
>> > @@ -127,7 +127,7 @@ namespace Coverage {
>> >    {
>> >      CoverageMaps::iterator cmi = coverageMaps.find( symbolName );
>> >      if ( cmi == coverageMaps.end() )
>> > -      throw rld::error (symbolName, "ExecutableInfo::findCoverageMap");
>> > +      throw CoverageMapNotFoundError(symbolName);
>> >      return *(cmi->second);
>> >    }
>> >
>> > diff --git a/tester/covoar/ExecutableInfo.h b/tester/covoar/ExecutableInfo.h
>> > index dcb4dcb..ab74b7f 100644
>> > --- a/tester/covoar/ExecutableInfo.h
>> > +++ b/tester/covoar/ExecutableInfo.h
>> > @@ -29,6 +29,10 @@ namespace Coverage {
>> >
>> >    public:
>> >
>> > +    class CoverageMapNotFoundError : public std::runtime_error {
>> > +      using std::runtime_error::runtime_error;
>> > +    };
>> > +
>> >      /*!
>> >       *  This method constructs an ExecutableInfo instance.
>> >       *
>> > diff --git a/tester/covoar/ObjdumpProcessor.cc b/tester/covoar/ObjdumpProcessor.cc
>> > index 56ee219..0647ff9 100644
>> > --- a/tester/covoar/ObjdumpProcessor.cc
>> > +++ b/tester/covoar/ObjdumpProcessor.cc
>> > @@ -32,35 +32,92 @@ namespace Coverage {
>> >      ObjdumpProcessor::objdumpLines_t instructions
>> >    ) {
>> >      // Find the symbol's coverage map.
>> > -    CoverageMapBase& coverageMap = executableInfo->findCoverageMap( symbolName );
>> > -
>> > -    uint32_t lowAddress = coverageMap.getFirstLowAddress();
>> > -    uint32_t size = coverageMap.getSize();
>> > -    uint32_t highAddress = lowAddress + size - 1;
>> > -
>> > -    // If there are NOT already saved instructions, save them.
>> > -    SymbolInformation* symbolInfo = SymbolsToAnalyze->find( symbolName );
>> > -    if (symbolInfo->instructions.empty()) {
>> > -      symbolInfo->sourceFile = executableInfo;
>> > -      symbolInfo->baseAddress = lowAddress;
>> > -      symbolInfo->instructions = instructions;
>> > -    }
>> > +    try {
>> > +      CoverageMapBase& coverageMap = executableInfo->findCoverageMap(symbolName);
>> >
>> > -    // Add the symbol to this executable's symbol table.
>> > -    SymbolTable* theSymbolTable = executableInfo->getSymbolTable();
>> > -    theSymbolTable->addSymbol(
>> > -      symbolName, lowAddress, highAddress - lowAddress + 1
>> > -    );
>> > +      uint32_t firstInstructionAddress = UINT32_MAX;
>> >
>> > -    // Mark the start of each instruction in the coverage map.
>> > -    for (auto& instruction : instructions) {
>> > -      coverageMap.setIsStartOfInstruction( instruction.address );
>> > -    }
>> > +      // Find the address of the first instruction.
>> > +      for (auto& line : instructions) {
>> > +        if (!line.isInstruction) {
>> > +          continue;
>> > +        }
>> > +
>> > +        firstInstructionAddress = line.address;
>> > +        break;
>> > +      }
>> This is a poorly written loop. Anytime you use continue/break to
>> control the loop iteration, something is going awry. You can write
>> this code equivalently without any continue or break.
>
>
> This loop is very deliberately constructed not to be a for loop because
> you need the ability to break at different points to debug issues. Alex
> and I spent hours in this loop debugging which I think would have
> been more difficult to debug if all the conditions were in a for loop.
> This loop was written to explicitly have breakpoints for various
> conditions.
>
> There have been multiple issues with the information reported with
> size/length being at the top of that list. Before Alex found the source
> of that (not covoar), it resulted in an infinite loop.
>
> I suppose it can be changed to a for loop as purer academically but
> it loses significantly on ability to set breakpoints and debug if there
> ever is an issue again.
>
> Are you willing to make that trade?
>

You know that what we debug is often much uglier than what we publish.
At least, simplify the logic to let the loop work for you.

for (auto& line : instructions) {
  if ( line.isInstruction ) {
    firstInstructionAddress = line.address;
    break;
  }
}

Using continue/break to control loops is antithetical to using loops,
and probably doesn't do the compiler any good.

>> > +        if (!line.isInstruction) {
>> > +          continue;
>> > +        }
>> > +
>> > +
>> > +        break;
>> > +      }
>>
>> > +
>> > +      if (firstInstructionAddress == UINT32_MAX) {
>> > +        std::ostringstream what;
>> > +        what << "Could not find first instruction address for symbol "
>> > +          << symbolName << " in " << executableInfo->getFileName();
>> > +        throw rld::error( what, "Coverage::finalizeSymbol" );
>> > +      }
>> > +
>> > +      int rangeIndex;
>> > +      uint32_t lowAddress = UINT32_MAX;
>> > +      for (rangeIndex = 0; ; rangeIndex++) {
>> > +        lowAddress = coverageMap.getLowAddressOfRange(rangeIndex);
>> > +
>> > +        if (firstInstructionAddress == lowAddress) {
>> > +          break;
>> ditto... if you're goin to break the loop on this condition, why not
>> use this in your for loop?
>> for (rangeIndex = 0; firstInstructionAddress != lowAddress ; rangeIndex++) {
>>
>> > +        }
>> > +      }
>> >
>> > -    // Create a unified coverage map for the symbol.
>> > -    SymbolsToAnalyze->createCoverageMap(
>> > -      executableInfo->getFileName().c_str(), symbolName, size
>> > -    );
>> > +      uint32_t sizeWithoutNops = coverageMap.getSizeOfRange(rangeIndex);
>> > +      uint32_t size = sizeWithoutNops;
>> > +      uint32_t highAddress = lowAddress + size - 1;
>> > +      uint32_t computedHighAddress = highAddress;
>> > +
>> > +      // Find the high address as reported by the address of the last NOP
>> > +      // instruction. This ensures that NOPs get marked as executed later.
>> > +      for (auto instruction = instructions.rbegin();
>> > +          instruction != instructions.rend();
>> > +          instruction++) {
>> > +        if (instruction->isInstruction) {
>> > +          if (instruction->isNop) {
>> > +            computedHighAddress = instruction->address + instruction->nopSize;
>> > +          }
>> > +          break;
>> Here too.
>>
>> > +        }
>> > +      }
>> > +
>> > +      if (highAddress != computedHighAddress) {
>> > +        std::cerr << "Function's high address differs between DWARF and objdump: "
>> > +          << symbolName << " (0x" << std::hex << highAddress << " and 0x"
>> > +          << computedHighAddress - 1 << ")" << std::dec << std::endl;
>> > +        size = computedHighAddress - lowAddress;
>> > +      }
>> > +
>> > +      // If there are NOT already saved instructions, save them.
>> > +      SymbolInformation* symbolInfo = SymbolsToAnalyze->find( symbolName );
>> > +      if (symbolInfo->instructions.empty()) {
>> > +        symbolInfo->sourceFile = executableInfo;
>> > +        symbolInfo->baseAddress = lowAddress;
>> > +        symbolInfo->instructions = instructions;
>> > +      }
>> > +
>> > +      // Add the symbol to this executable's symbol table.
>> > +      SymbolTable* theSymbolTable = executableInfo->getSymbolTable();
>> > +      theSymbolTable->addSymbol(
>> > +        symbolName, lowAddress, highAddress - lowAddress + 1
>> > +      );
>> > +
>> > +      // Mark the start of each instruction in the coverage map.
>> > +      for (auto& instruction : instructions) {
>> > +        coverageMap.setIsStartOfInstruction( instruction.address );
>> > +      }
>> > +
>> > +      // Create a unified coverage map for the symbol.
>> > +      SymbolsToAnalyze->createCoverageMap(
>> > +        executableInfo->getFileName().c_str(), symbolName, size, sizeWithoutNops
>> > +      );
>> > +    } catch (const ExecutableInfo::CoverageMapNotFoundError& e) {
>> > +      // Allow execution to continue even if a coverage map could not be
>> > +      // found.
>> > +      std::cerr << "Coverage map not found for symbol " << e.what()
>> > +        << std::endl;
>> > +    }
>> >    }
>> >
>> >    ObjdumpProcessor::ObjdumpProcessor()
>> > --
>> > 2.27.0
>> >
>> > _______________________________________________
>> > devel mailing list
>> > devel at rtems.org
>> > http://lists.rtems.org/mailman/listinfo/devel
>> _______________________________________________
>> devel mailing list
>> devel at rtems.org
>> http://lists.rtems.org/mailman/listinfo/devel


More information about the devel mailing list