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
This commit is contained in:
Alex White 2021-04-30 14:34:18 -05:00 committed by Joel Sherrill
parent a88be93a88
commit ac56fce7c1
7 changed files with 377 additions and 51 deletions

View File

@ -1893,6 +1893,11 @@ namespace rld
return false;
}
const addresses& compilation_unit::get_addresses () const
{
return addr_lines_;
}
functions&
compilation_unit::get_functions ()
{

View File

@ -707,6 +707,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

View File

@ -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();
}
}

View File

@ -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

View File

@ -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;
}
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.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;
}
}
// 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);
}
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);
}
} 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.
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.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;
}
}
// 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);
}
}
}
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 ();

View File

@ -15,6 +15,7 @@
#include <rld-files.h>
#include <rld-symbols.h>
#include "AddressToLineMapper.h"
#include "CoverageMapBase.h"
#include "SymbolTable.h"
@ -157,11 +158,6 @@ namespace Coverage {
uint32_t highAddress
);
/*!
* The DWARF data to the ELF executable.
*/
rld::dwarf::file debug;
/*!
* The executable's file name.
*/
@ -172,6 +168,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.
*/

View File

@ -83,6 +83,7 @@ def build(bld):
bld.stlib(target = 'ccovoar',
source = ['app_common.cc',
'AddressToLineMapper.cc',
'CoverageFactory.cc',
'CoverageMap.cc',
'CoverageMapBase.cc',