summaryrefslogtreecommitdiff
path: root/libdw/c++/dwarf_output
diff options
context:
space:
mode:
Diffstat (limited to 'libdw/c++/dwarf_output')
-rw-r--r--libdw/c++/dwarf_output266
1 files changed, 201 insertions, 65 deletions
diff --git a/libdw/c++/dwarf_output b/libdw/c++/dwarf_output
index b7bbaadc..aeb192c4 100644
--- a/libdw/c++/dwarf_output
+++ b/libdw/c++/dwarf_output
@@ -151,6 +151,12 @@ namespace elfutils
friend class dwarf_output;
friend class dwarf_output_collector;
+ __attribute__((used)) die_info_pair *info () const
+ {
+ return reinterpret_cast<die_info_pair *>
+ (const_cast<debug_info_entry *> (this));
+ }
+
public:
class attributes_type
: public dwarf_data::attributes_type<dwarf_output, value>
@@ -403,13 +409,18 @@ namespace elfutils
: _base (i, dummy)
{}
- /* The hash of a value_reference is its referent's identity,
- because we can have multiple value_reference objects that
- wind up pointing to the same entry. This method is virtual
- for the circular_reference case, below. */
- virtual size_t hash () const
+ /* The hash of a value_reference is its referent's local hash,
+ which only takes non-reference values into account. This
+ method is virtual for the circular_reference case, below. */
+ inline size_t hash () const
{
- return ref->identity ();
+ struct die_info *info = get_die_info ();
+ return info->_m_reference_hash;
+ }
+
+ virtual die_info *get_die_info () const
+ {
+ return &ref->info ()->second;
}
};
@@ -584,10 +595,21 @@ namespace elfutils
std::bitset<2> _m_with_sibling;
unsigned int _m_uses;
- inline die_info ()
+ /* The local hash of the die, set when die_info is created.
+ Uses only none-reference values. */
+ size_t _m_local_hash;
+
+ /* The reference hash of the die, based on just reference
+ attribute values. Needs all local hashes of referenced dies
+ to be set. */
+ size_t _m_reference_hash;
+
+ inline die_info (size_t local_hash)
: _m_parent (NULL), _m_refs (),
_m_originals (), _m_original_cost (0),
- _m_with_sibling (), _m_uses (0)
+ _m_with_sibling (), _m_uses (0),
+ _m_local_hash (local_hash),
+ _m_reference_hash (0)
{}
inline ~die_info ()
@@ -982,11 +1004,12 @@ namespace elfutils
inline die_info_pair *add_entry (int tag,
const children_type *children,
- const attrs_type *attrs)
+ const attrs_type *attrs,
+ die_info *info)
{
std::pair <die_map::iterator, bool>
ins = _m_unique.insert (std::make_pair (die_type (tag, children, attrs),
- die_info ()));
+ *info));
die_info_pair &x = *ins.first;
if (ins.second)
x.second.assert_unused ();
@@ -994,39 +1017,6 @@ namespace elfutils
return &x;
}
- struct shape_type
- {
- typedef std::vector<std::pair<int, int> > attrs_type;
- attrs_type _m_attrs;
- bool _m_has_children;
- size_t _m_hash;
-
- friend class subr::hashed_hasher<shape_type>;
- typedef subr::hashed_hasher<shape_type> hasher;
-
- inline void hashnadd (int name, int form);
- inline shape_type (const die_type &die, bool last_sibling);
-
- inline bool operator== (const shape_type &other) const
- {
- return (_m_hash == other._m_hash
- && _m_has_children == other._m_has_children
- && _m_attrs == other._m_attrs);
- }
- inline bool operator!= (const shape_type &other) const
- {
- return !(*this == other);
- }
- };
-
- typedef subr::nothing shape_info;
-
- typedef std::tr1::unordered_map<shape_type, shape_info,
- shape_type::hasher> shape_map;
- shape_map _m_shapes;
-
- void add_shape (die_type &die, bool last_sibling);
-
struct stats_cmp
: public std::binary_function<const die_map::value_type *,
const die_map::value_type *,
@@ -1206,6 +1196,7 @@ namespace elfutils
{
private:
const std::vector<entry *> *_m_entry;
+ bool _m_final;
inline circular_reference (const circular_reference &)
: value::value_reference ()
@@ -1216,14 +1207,16 @@ namespace elfutils
public:
inline circular_reference (const entry *die, copier *)
: value::value_reference (),
- _m_entry (new std::vector<entry *> (1, const_cast<entry *> (die)))
+ _m_entry (new std::vector<entry *> (1, const_cast<entry *> (die))),
+ _m_final (false)
{
die->dump () << " new circular_reference " << this << "\n";
}
inline bool final () const
{
- return _m_entry == NULL;
+ // XXX was return _m_entry == NULL;
+ return _m_final;
}
inline typename std::vector<entry *>::const_iterator pending () const
@@ -1241,8 +1234,10 @@ namespace elfutils
{
pending_entry ()->dump () << " placed circular_reference "
<< this << "\n";
- delete _m_entry;
- _m_entry = NULL;
+ // XXX Keeping around for local hash...
+ _m_final = true;
+ // delete _m_entry;
+ // _m_entry = NULL;
}
inline ~circular_reference ()
@@ -1256,14 +1251,11 @@ namespace elfutils
}
/* We have a special case for a reference attribute that is part
- of a circular chain. That value always hashes as zero, so that
- each entry in a circular chain of references has the same hash
- value as any entry that it otherwise matches and that has any
- (eventually) circular reference as the corresponding
- attribute's value. */
- virtual size_t hash () const
+ of a circular chain. It gets calculated from the
+ pending_entry. */
+ virtual die_info *get_die_info () const
{
- return 0;
+ return pending_entry ()->get_die_info ();
}
};
@@ -1298,9 +1290,14 @@ namespace elfutils
// Set if _m_children contains any entries not already final.
bool _m_unfinished_children;
+ // The die_info that will be used when putting the die into
+ // the collector. Stores local hash as soon as all children
+ // are defined in defined_self ().
+ die_info *_m_info;
+
inline pending_entry (int tag)
: _m_finalizing (NULL), _m_self (NULL), _m_matched (NULL),
- _m_tag (tag), _m_unfinished_children (false)
+ _m_tag (tag), _m_unfinished_children (false), _m_info (NULL)
{}
inline ~pending_entry ()
@@ -1385,6 +1382,84 @@ namespace elfutils
typedef typename final_children_getter::
template copy<debug_info_entry::children_type> get_final_children;
+ inline size_t get_reference_hash (std::vector<const entry *> &stack) const
+ {
+ assert (_m_info != NULL);
+
+ // Could already have been set by forward reference.
+ if (_m_info->_m_reference_hash != 0)
+ return _m_info->_m_reference_hash;
+
+ size_t rhash = _m_info->_m_local_hash;
+ size_t attr_rhashes = 0;
+ for (attr_map::const_iterator it = _m_attributes.begin ();
+ it != _m_attributes.end ();
+ ++it)
+ {
+ // XXX XOR is for order irrelevance, but shouldn't be necessary.
+ // See also calculate_local_hash, which does the same.
+ const entry *ref
+ = dynamic_cast<const entry *> (it->second._m_value);
+ if (ref != NULL)
+ {
+ // Workaround weird case (bug?)
+ // https://fedorahosted.org/pipermail/elfutils-devel/2011-February/001792.html
+ if (it->first == DW_AT_containing_type
+ && ref->_m_pending == this)
+ continue;
+ else
+ attr_rhashes ^= ref->get_reference_hash (stack);
+ }
+ }
+ subr::hash_combine (rhash, attr_rhashes);
+
+ return rhash;
+ }
+
+ inline size_t calculate_local_hash ()
+ {
+ // Calculate the local hash for this new die.
+ // XOR the attribute values together (so order doesn't matter)
+ // but exclude reference attributes values (just include
+ // their tag). XXX - shouldn't be necessary, double check.
+ size_t lhash = _m_tag;
+ size_t attr_hash = 0;
+ for (attr_map::const_iterator it = _m_attributes.begin ();
+ it != _m_attributes.end ();
+ ++it)
+ {
+ if (it->second.what_space () != dwarf::VS_reference)
+ attr_hash ^= subr::hash_this (*it);
+ else
+ attr_hash ^= (it->first << 3);
+ }
+ subr::hash_combine (lhash, attr_hash);
+
+ size_t children_hash = 0;
+ for (typename std::vector<entry *>::const_iterator it
+ = _m_children.begin ();
+ it != _m_children.end ();
+ ++it)
+ {
+ // child lhash is always in the die_info, which might
+ // be in the pending_entry when not yet finalized, or
+ // part of the finalized child die_info.
+ size_t child_lhash;
+ struct pending_entry *pending = (*it)->_m_pending;
+ if (pending)
+ child_lhash = pending->_m_info->_m_local_hash;
+ else
+ {
+ die_info_pair *final_child = get_final_child (*it);
+ child_lhash = final_child->second._m_local_hash;
+ }
+ subr::hash_combine (children_hash, child_lhash);
+ }
+ subr::hash_combine (lhash, children_hash);
+
+ return lhash;
+ }
+
inline die_info_pair *final (copier *c,
::Dwarf_Off offset, ::Dwarf_Off cost)
{
@@ -1404,7 +1479,7 @@ namespace elfutils
(_m_children, std::ptr_fun (get_final_child)),
fresh);
- _m_matched = co->add_entry (_m_tag, children, attrs);
+ _m_matched = co->add_entry (_m_tag, children, attrs, _m_info);
}
// Final bookkeeping in the collector for this copied entry.
@@ -1514,7 +1589,7 @@ namespace elfutils
built in the output has one of these in place of a value_reference.
These all live in the _m_entries map, one per input-side DIE. */
struct entry
- : public value::value_dispatch
+ : public value::value_reference
{
::Dwarf_Off _m_offset; // For debugging and statistics only.
::Dwarf_Off _m_cost; // For statistics only.
@@ -1603,10 +1678,9 @@ namespace elfutils
}
/* We are no longer an undefined entry, so decrement the count.
- Then finalize as much as we can now. We attempt finalization
- even when the count is nonzero, so that a leaf entry with no
- forward references finishes immediately, and so then can its
- parents and on up if they don't own any pending references. */
+ But don't finalize yet. Since all children are known now we
+ can create a candidate die_info that includes the local hash
+ for this entry. */
inline void defined_self (copier *c)
{
assert (_m_final == NULL);
@@ -1614,9 +1688,9 @@ namespace elfutils
assert (c->_m_undefined_entries > 0);
--c->_m_undefined_entries;
dump () << " defined_self => " << c->_m_undefined_entries << "\n";
- finalize (c);
- if (_m_final == NULL)
- assert (c->_m_undefined_entries > 0);
+
+ size_t lhash = _m_pending->calculate_local_hash ();
+ _m_pending->_m_info = new die_info (lhash);
}
/* A reference-following matching operation noticed along
@@ -1649,6 +1723,48 @@ namespace elfutils
*p.second = true;
}
+ /* Recursively sets up reference hashes for the die_info of this
+ pending_entry. Depends on all local hashes having been setup
+ already. At this point all entries are still pending. */
+ inline void setup_reference_hash () const
+ {
+ std::vector<const entry *> stack;
+ _m_pending->_m_info->_m_reference_hash = get_reference_hash (stack);
+ assert (stack.empty ());
+
+ for (typename std::vector<entry *>::const_iterator it
+ = _m_pending->_m_children.begin ();
+ it != _m_pending->_m_children.end ();
+ ++it)
+ (*it)->setup_reference_hash ();
+ }
+
+ inline size_t get_reference_hash (std::vector<const entry *> &stack) const
+ {
+ if (std::find (stack.begin (), stack.end (), this) != stack.end ())
+ {
+ std::cout << "Reference chain cycle detected\n"
+ << "offset=[0x" << std::hex << _m_offset << std::dec
+ << "] already on the reference chain stack\n";
+ typename std::vector<const entry *>::iterator it;
+ for (it = stack.begin ();
+ it != stack.end ();
+ it++)
+ {
+ std::cout << "offset=[0x" << std::hex << (*it)->_m_offset
+ << std::dec << "] "
+ << dwarf::tags::name ((*it)->_m_pending->_m_tag)
+ << "\n";
+ }
+ abort ();
+ }
+
+ stack.push_back (this);
+ size_t res = _m_pending->get_reference_hash (stack);
+ stack.pop_back ();
+ return res;
+ }
+
// Attempt to turn the pending entry into a final entry.
void finalize (copier *c)
{
@@ -1780,6 +1896,15 @@ namespace elfutils
assert (_m_pending != NULL);
return _m_parent->_m_pending->child (_m_self_idx);
}
+
+ /* The local hash of the debug_info_entry if we are already
+ final, otherwise get it from the pending_entry. */
+ inline die_info *get_die_info () const
+ {
+ if (_m_final)
+ return &_m_final->second;
+ return _m_pending->_m_info;
+ }
};
// This object lives while we are copying one particular input DIE.
@@ -1874,11 +1999,22 @@ namespace elfutils
return *_m_copier;
}
- /* Complain if we still have dangling references.
+ /* Walk over the whole cu again to set reference hashes up.
+ Then try to finalize everything at once.
+ Complain if we still have dangling references.
If not, it should be impossible to have pending entries left. */
inline die_info_pair *final_unit () const
{
assert (_m_out == NULL);
+
+ // First walk over the whole CU tree again to setup the
+ // reference hash for each die_info.
+ _m_in->setup_reference_hash ();
+
+ // We should now be able to finalize everything at once.
+ if (_m_copier->_m_undefined_entries == 0)
+ _m_in->finalize (_m_copier);
+
if (unlikely (_m_in->_m_final == NULL))
{
_m_in->dump_entry ();