[rtems-tools commit] covoar: Store address-to-line info outside of DWARF

Joel Sherrill joel at rtems.org
Thu Jun 17 21:05:34 UTC 2021


Module:    rtems-tools
Branch:    master
Commit:    ac56fce7c17e40c6ecd7980fa828a76550bbacb3
Changeset: http://git.rtems.org/rtems-tools/commit/?id=ac56fce7c17e40c6ecd7980fa828a76550bbacb3

Author:    Alex White <alex.white at oarcorp.com>
Date:      Fri Apr 30 14:34:18 2021 -0500

covoar: Store address-to-line info outside of DWARF

This adds the AddressToLineMapper class and supporting classes to
assume responsibility of tracking address-to-line information.

This allows the DWARF library to properly cleanup all of its resources
and leads to significant memory savings.

Closes #4383

---

 rtemstoolkit/rld-dwarf.cpp           |   5 +
 rtemstoolkit/rld-dwarf.h             |   5 +
 tester/covoar/AddressToLineMapper.cc | 104 +++++++++++++++++
 tester/covoar/AddressToLineMapper.h  | 211 +++++++++++++++++++++++++++++++++++
 tester/covoar/ExecutableInfo.cc      |  73 ++++++------
 tester/covoar/ExecutableInfo.h       |  11 +-
 tester/covoar/wscript                |   1 +
 7 files changed, 368 insertions(+), 42 deletions(-)

diff --git a/rtemstoolkit/rld-dwarf.cpp b/rtemstoolkit/rld-dwarf.cpp
index 2fce0e4..1eae50c 100644
--- a/rtemstoolkit/rld-dwarf.cpp
+++ b/rtemstoolkit/rld-dwarf.cpp
@@ -1893,6 +1893,11 @@ namespace rld
       return false;
     }
 
+    const addresses& compilation_unit::get_addresses () const
+    {
+      return addr_lines_;
+    }
+
     functions&
     compilation_unit::get_functions ()
     {
diff --git a/rtemstoolkit/rld-dwarf.h b/rtemstoolkit/rld-dwarf.h
index 1210813..eadb50d 100644
--- a/rtemstoolkit/rld-dwarf.h
+++ b/rtemstoolkit/rld-dwarf.h
@@ -708,6 +708,11 @@ namespace rld
       unsigned int pc_high () const;
 
       /**
+       * The addresses associated with this compilation unit.
+       */
+      const addresses& get_addresses () const;
+
+      /**
        * Get the source and line for an address. If the address does not match
        * false is returned the file is set to 'unknown' and the line is set to
        * 0.
diff --git a/tester/covoar/AddressToLineMapper.cc b/tester/covoar/AddressToLineMapper.cc
new file mode 100644
index 0000000..c305e3b
--- /dev/null
+++ b/tester/covoar/AddressToLineMapper.cc
@@ -0,0 +1,104 @@
+/*! @file AddressToLineMapper.cc
+ *  @brief AddressToLineMapper Implementation
+ *
+ *  This file contains the implementation of the functionality
+ *  of the AddressToLineMapper class.
+ */
+
+#include "AddressToLineMapper.h"
+
+namespace Coverage {
+
+  uint64_t SourceLine::location() const
+  {
+    return address;
+  }
+
+  bool SourceLine::is_an_end_sequence() const
+  {
+    return is_end_sequence;
+  }
+
+  const std::string& SourceLine::path() const
+  {
+    return path_;
+  }
+
+  int SourceLine::line() const
+  {
+    return line_num;
+  }
+
+  void AddressLineRange::addSourceLine(const rld::dwarf::address& address)
+  {
+    auto insertResult = sourcePaths.insert(address.path());
+
+    sourceLines.emplace_back(
+      SourceLine (
+        address.location(),
+        *insertResult.first,
+        address.line(),
+        address.is_an_end_sequence()
+      )
+    );
+  }
+
+  const SourceLine& AddressLineRange::getSourceLine(uint32_t address) const
+  {
+    if (address < lowAddress || address > highAddress) {
+      throw SourceNotFoundError(std::to_string(address));
+    }
+
+    const SourceLine* last_line = nullptr;
+    for (const auto &line : sourceLines) {
+      if (address <= line.location())
+      {
+        if (address == line.location())
+          last_line = &line;
+        break;
+      }
+      last_line = &line;
+    }
+
+    if (last_line == nullptr) {
+      throw SourceNotFoundError(std::to_string(address));
+    }
+
+    return *last_line;
+  }
+
+  void AddressToLineMapper::getSource(
+    uint32_t address,
+    std::string& sourceFile,
+    int& sourceLine
+  ) const {
+    const SourceLine default_sourceline = SourceLine();
+    const SourceLine* match = &default_sourceline;
+
+    for (const auto &range : addressLineRanges) {
+      try {
+        const SourceLine& potential_match = range.getSourceLine(address);
+
+        if (match->is_an_end_sequence() || !potential_match.is_an_end_sequence()) {
+          match = &potential_match;
+        }
+      } catch (const AddressLineRange::SourceNotFoundError&) {}
+    }
+
+    sourceFile = match->path();
+    sourceLine = match->line();
+  }
+
+  AddressLineRange& AddressToLineMapper::makeRange(
+    uint32_t low,
+    uint32_t high
+  )
+  {
+    addressLineRanges.emplace_back(
+      AddressLineRange(low, high)
+    );
+
+    return addressLineRanges.back();
+  }
+
+}
diff --git a/tester/covoar/AddressToLineMapper.h b/tester/covoar/AddressToLineMapper.h
new file mode 100644
index 0000000..88bf475
--- /dev/null
+++ b/tester/covoar/AddressToLineMapper.h
@@ -0,0 +1,211 @@
+/*! @file AddressToLineMapper.h
+ *  @brief AddressToLineMapper Specification
+ *
+ *  This file contains the specification of the AddressToLineMapper class.
+ */
+
+#ifndef __ADDRESS_TO_LINE_MAPPER_H__
+#define __ADDRESS_TO_LINE_MAPPER_H__
+
+#include <cstdint>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <rld-dwarf.h>
+
+namespace Coverage {
+
+  /*! @class SourceLine
+   *
+   *  This class stores source information for a specific address.
+   */
+  class SourceLine {
+
+  public:
+
+    SourceLine()
+    : address(0),
+      path_("unknown"),
+      line_num(-1),
+      is_end_sequence(true)
+    {
+    }
+
+    SourceLine(
+      uint64_t addr,
+      const std::string& src,
+      int line,
+      bool end_sequence
+    ) : address(addr),
+        path_(src),
+        line_num(line),
+        is_end_sequence(end_sequence)
+    {
+    }
+
+    /*!
+     *  This method gets the address of this source information.
+     *
+     *  @return Returns the address of this source information
+     */
+    uint64_t location() const;
+
+    /*!
+     *  This method gets a value indicating whether this address represents an
+     *  end sequence.
+     *
+     *  @return Returns whether this address represents an end sequence or not
+     */
+    bool is_an_end_sequence() const;
+
+    /*!
+     *  This method gets the source file path of this address.
+     *
+     *  @return Returns the source file path of this address
+     */
+    const std::string& path() const;
+
+    /*!
+     *  This method gets the source line number of this address.
+     *
+     *  @return Returns the source line number of this address
+     */
+    int line() const;
+
+  private:
+
+    /*!
+     *  The address of this source information.
+     */
+    uint64_t address;
+
+    /*!
+     *  An iterator pointing to the location in the set that contains the
+     *  source file path of the address.
+     */
+    const std::string& path_;
+
+    /*!
+     *  The source line number of the address.
+     */
+    int line_num;
+
+    /*!
+     *  Whether the address represents an end sequence or not.
+     */
+    bool is_end_sequence;
+
+  };
+
+  typedef std::vector<SourceLine> SourceLines;
+
+  typedef std::set<std::string> SourcePaths;
+
+  /*! @class AddressLineRange
+   *
+   *  This class stores source information for an address range.
+   */
+  class AddressLineRange {
+
+  public:
+
+    class SourceNotFoundError : public std::runtime_error {
+      /* Use the base class constructors. */
+      using std::runtime_error::runtime_error;
+    };
+
+    AddressLineRange(
+      uint32_t low,
+      uint32_t high
+    ) : lowAddress(low), highAddress(high)
+    {
+    }
+
+    /*!
+     *  This method adds source and line information for a specified address.
+     *
+     *  @param[in] address specifies the DWARF address information
+     */
+    void addSourceLine(const rld::dwarf::address& address);
+
+    /*!
+     *  This method gets the source file name and line number for a given
+     *  address.
+     *
+     *  @param[in] address specifies the address to look up
+     *
+     *  @return Returns the source information for the specified address.
+     */
+    const SourceLine& getSourceLine(uint32_t address) const;
+
+  private:
+
+    /*!
+     *  The low address for this range.
+     */
+    uint32_t lowAddress;
+
+    /*!
+     *  The high address for this range.
+     */
+    uint32_t highAddress;
+
+    /*!
+     *  The source information for addresses in this range.
+     */
+    SourceLines sourceLines;
+
+    /*!
+     *  The set of source file names for this range.
+     */
+    SourcePaths sourcePaths;
+
+  };
+
+  typedef std::vector<AddressLineRange> AddressLineRanges;
+
+  /*! @class AddressToLineMapper
+   *
+   *  This class provides address-to-line capabilities.
+   */
+  class AddressToLineMapper {
+
+  public:
+
+    /*!
+     *  This method gets the source file name and line number for a given
+     *  address.
+     *
+     *  @param[in] address specifies the address to look up
+     *  @param[out] sourceFile specifies the name of the source file
+     *  @param[out] sourceLine specifies the line number in the source file
+     */
+    void getSource(
+      uint32_t address,
+      std::string& sourceFile,
+      int& sourceLine
+    ) const;
+
+    /*!
+     *  This method creates a new range with the specified addresses.
+     *
+     *  @param[in] low specifies the low address of the range
+     *  @param[in] high specifies the high address of the range
+     *
+     *  @return Returns a reference to the newly created range
+     */
+    AddressLineRange& makeRange(uint32_t low, uint32_t high);
+
+  private:
+
+    /*!
+     *  The address and line information ranges.
+     */
+    AddressLineRanges addressLineRanges;
+
+  };
+
+}
+
+#endif
diff --git a/tester/covoar/ExecutableInfo.cc b/tester/covoar/ExecutableInfo.cc
index 861e60d..a6184e7 100644
--- a/tester/covoar/ExecutableInfo.cc
+++ b/tester/covoar/ExecutableInfo.cc
@@ -40,61 +40,60 @@ namespace Coverage {
     executable.begin();
     executable.load_symbols(symbols);
 
+    rld::dwarf::file debug;
+
     debug.begin(executable.elf());
     debug.load_debug();
     debug.load_functions();
 
-    try {
-      for (auto& cu : debug.get_cus()) {
-        for (auto& func : cu.get_functions()) {
-          if (!func.has_machine_code()) {
-            continue;
-          }
+    for (auto& cu : debug.get_cus()) {
+      AddressLineRange& range = mapper.makeRange(cu.pc_low(), cu.pc_high());
+      // Does not filter on desired symbols under the assumption that the test
+      // code and any support code is small relative to what is being tested.
+      for (const auto &address : cu.get_addresses()) {
+        range.addSourceLine(address);
+      }
 
-          if (!SymbolsToAnalyze->isDesired(func.name())) {
-            continue;
+      for (auto& func : cu.get_functions()) {
+        if (!func.has_machine_code()) {
+          continue;
+        }
+
+        if (!SymbolsToAnalyze->isDesired(func.name())) {
+          continue;
+        }
+
+        if (func.is_inlined()) {
+          if (func.is_external()) {
+            // Flag it
+            std::cerr << "Function is both external and inlined: "
+                      << func.name() << std::endl;
           }
 
-          if (func.is_inlined()) {
-            if (func.is_external()) {
-              // Flag it
-              std::cerr << "Function is both external and inlined: "
-                        << func.name() << std::endl;
-            }
-
-            if (func.has_entry_pc()) {
-              continue;
-            }
-
-            // If the low PC address is zero, the symbol does not appear in
-            // this executable.
-            if (func.pc_low() == 0) {
-              continue;
-            }
+          if (func.has_entry_pc()) {
+            continue;
           }
 
-          // We can't process a zero size function.
-          if (func.pc_high() == 0) {
+          // If the low PC address is zero, the symbol does not appear in
+          // this executable.
+          if (func.pc_low() == 0) {
             continue;
           }
+        }
 
-          createCoverageMap (cu.name(), func.name(),
-                              func.pc_low(), func.pc_high() - 1);
+        // We can't process a zero size function.
+        if (func.pc_high() == 0) {
+          continue;
         }
+
+        createCoverageMap (cu.name(), func.name(),
+                            func.pc_low(), func.pc_high() - 1);
       }
-    } catch (...) {
-      debug.end();
-      throw;
     }
-
-    // Can't cleanup handles until the destructor because the information is
-    // referenced elsewhere. NOTE: This could cause problems from too many open
-    // file descriptors.
   }
 
   ExecutableInfo::~ExecutableInfo()
   {
-    debug.end();
   }
 
   void ExecutableInfo::dumpCoverageMaps( void ) {
@@ -197,7 +196,7 @@ namespace Coverage {
   {
     std::string file;
     int         lno;
-    debug.get_source (address, file, lno);
+    mapper.getSource (address, file, lno);
     std::ostringstream ss;
     ss << file << ':' << lno;
     line = ss.str ();
diff --git a/tester/covoar/ExecutableInfo.h b/tester/covoar/ExecutableInfo.h
index 5d5a595..dfc71aa 100644
--- a/tester/covoar/ExecutableInfo.h
+++ b/tester/covoar/ExecutableInfo.h
@@ -15,6 +15,7 @@
 #include <rld-files.h>
 #include <rld-symbols.h>
 
+#include "AddressToLineMapper.h"
 #include "CoverageMapBase.h"
 #include "SymbolTable.h"
 
@@ -158,11 +159,6 @@ namespace Coverage {
     );
 
     /*!
-     *  The DWARF data to the ELF executable.
-     */
-    rld::dwarf::file debug;
-
-    /*!
      *  The executable's file name.
      */
     std::string fileName;
@@ -173,6 +169,11 @@ namespace Coverage {
     rld::symbols::table symbols;
 
     /*!
+     *  The address-to-line mapper for this executable.
+     */
+    AddressToLineMapper mapper;
+
+    /*!
      *  This map associates a symbol with its coverage map.
      */
     typedef std::map<std::string, CoverageMapBase *> CoverageMaps;
diff --git a/tester/covoar/wscript b/tester/covoar/wscript
index 82599b0..a928b1b 100644
--- a/tester/covoar/wscript
+++ b/tester/covoar/wscript
@@ -83,6 +83,7 @@ def build(bld):
 
     bld.stlib(target = 'ccovoar',
               source = ['app_common.cc',
+                        'AddressToLineMapper.cc',
                         'CoverageFactory.cc',
                         'CoverageMap.cc',
                         'CoverageMapBase.cc',



More information about the vc mailing list