summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2009-08-10 03:06:28 -0700
committerRoland McGrath <roland@redhat.com>2009-08-10 03:06:28 -0700
commit5d32ab922f069c5a7ea73cebc2a9fcc6fe10b097 (patch)
treef91df66750270c5d40a6fe2dd41a43c0f09952b5
parent42857a4a49990a94c832a11452355f14056af7a9 (diff)
downloadelfutils-5d32ab922f069c5a7ea73cebc2a9fcc6fe10b097.tar.gz
On the verge of doing circular refs
-rw-r--r--libdw/c++/dwarf_data4
-rw-r--r--libdw/c++/dwarf_output238
2 files changed, 191 insertions, 51 deletions
diff --git a/libdw/c++/dwarf_data b/libdw/c++/dwarf_data
index 742e3741..2a2dead8 100644
--- a/libdw/c++/dwarf_data
+++ b/libdw/c++/dwarf_data
@@ -1201,12 +1201,14 @@ namespace elfutils
}
public:
+#if 0 // XXX
attr_value (const attr_value &other)
: _m_value (NULL)
{
if (other._m_value != NULL)
init<attr_value> me (this, 0, other);
}
+#endif
inline attr_value ()
: _m_value (NULL)
@@ -1218,6 +1220,7 @@ namespace elfutils
delete _m_value;
}
+#if 0 // XXX
inline attr_value &operator= (const attr_value &other)
{
if (_m_value != NULL)
@@ -1239,6 +1242,7 @@ namespace elfutils
init<value> me (this, 0, other);
return *this;
}
+#endif
dwarf::value_space what_space () const;
inline std::string to_string () const;
diff --git a/libdw/c++/dwarf_output b/libdw/c++/dwarf_output
index ac58b64e..c317f215 100644
--- a/libdw/c++/dwarf_output
+++ b/libdw/c++/dwarf_output
@@ -1017,6 +1017,11 @@ namespace elfutils
attributes or dangling child entries. Each dangling attribute and
each dangling child contributes one to _m_dangling_count.
+ A new pending_entry still being made in entry_copier::populate
+ starts with a dangling/pending count of one to represent that which
+ will be added. This prevents one sibling that completes another
+ from trying to complete its parent before we've finished building it.
+
*/
struct seen; // Below.
@@ -1034,10 +1039,22 @@ namespace elfutils
inline pending_entry (int tag)
: _m_attributes (), _m_children (), _m_tag (tag),
- _m_dangling_count (0), _m_pending_count (0),
+ _m_dangling_count (1), _m_pending_count (1),
_m_parents ()
{}
+ inline void dump (const seen *me) const
+ {
+ me->dump (true) << " pending " << dwarf::tags::identifier (_m_tag)
+ << " " << _m_dangling_count
+ << "/" << _m_pending_count << "\n";
+ for (typename std::deque<seen *>::const_iterator
+ i = _m_parents.begin (); i != _m_parents.end (); ++i)
+ (*i)->dump () << " parent waits\n";
+ me->dump_refs ();
+ me->dump (false, true) << " ends\n";
+ }
+
// Count one pending or dangling attribute or child.
inline void count_pending (bool dangle)
{
@@ -1073,10 +1090,14 @@ namespace elfutils
}
// One of our pending attributes or children is final now.
- inline bool resolve_pending ()
+ inline bool resolve_pending (bool was_dangling)
{
- assert (_m_pending_count > _m_dangling_count);
assert (_m_pending_count > 0);
+ assert (_m_pending_count >= _m_dangling_count);
+ if (was_dangling)
+ --_m_dangling_count;
+ else
+ assert (_m_pending_count > _m_dangling_count);
return --_m_pending_count == 0;
}
@@ -1134,11 +1155,11 @@ namespace elfutils
return c->add_entry (_m_tag, children, attrs);
}
- inline void resolve_parents (copier *c)
+ inline void resolve_parents (copier *c, bool was_dangling)
{
while (!_m_parents.empty ())
{
- _m_parents.front ()->resolve_pending (c);
+ _m_parents.front ()->resolve_pending (c, was_dangling);
_m_parents.pop_front ();
}
}
@@ -1172,9 +1193,16 @@ namespace elfutils
: _m_building (NULL), _m_final (NULL), _m_pending (NULL), _m_patch ()
{}
+ inline ~seen ()
+ {
+ assert (_m_building == NULL);
+ // This should only hit in an exception case abandoning the copier.
+ if (unlikely (_m_pending != NULL))
+ delete _m_pending;
+ }
+
/* Called by entry_copier::add_reference, below.
We're adding a reference attribute pointing to this input entry. */
-
inline const value::value_dispatch *
refer (seen *referrer, const value::value_dispatch **backptr)
{
@@ -1191,70 +1219,149 @@ namespace elfutils
return this;
}
+ std::ostream &dump (bool in = false, bool out = false) const
+ {
+ static int depth;
+ depth -= out;
+ std::cout << std::string (depth, ' ')
+ << "XXX " << std::hex << _m_offset << std::dec;
+ depth += in;
+ return std::cout;
+ }
+
+ inline void dump_pending () const
+ {
+ if (_m_final == NULL)
+ _m_pending->dump (this);
+ }
+
/* One dangling attribute or child is no longer dangling.
See if that completes us. */
inline void resolve_dangling (copier *c, bool final)
{
- if (_m_pending->resolve_dangling (final)
+ if (!_m_pending->dangling ())
+ {
+ assert (!final);
+
+ /* We're being called from resolve_ref, below. But our pending
+ entry is in fact not dangling. This means we're the root of
+ a circularity in the reference graph. A referrer is telling
+ us that it's no longer dangling, but we ourselves triggered
+ its conversion when we stopped dangling. */
+ dump () << " circularity!\n";
+ return;
+ }
+
+ if (_m_pending->resolve_dangling (final))
+ {
// We no longer have any dangling references!
- && !made (c))
+ dump () << " resolved with "
+ << _m_pending->_m_pending_count << " pending\n";
+
+ promote_pending (c, _m_pending->complete (), true);
+ }
+ else
+ dump () << " unresolved with "
+ << _m_pending->_m_dangling_count << "/"
+ << _m_pending->_m_pending_count << "\n";
+ }
+
+ /* The pending_entry is no longer dangling.
+ Promote it to pending or final. */
+ inline void promote_pending (copier *c, bool final, bool was_dangling)
+ {
+ dump () << " no longer dangling ("
+ << c->_m_defined + 1 << " of " << c->_m_seen.size () << "), "
+ << _m_pending->_m_pending_count << " pending\n";
+
+ if (final)
+ // It's all done. Finish up all our references.
+ finish_pending (c, was_dangling);
+ else
{
- // We are still pending ourselves.
- _m_pending->parents_resolve_dangling (c);
+ /* It's still pending, but no longer dangling.
+ Adjust bookkeeping. We are still pending ourselves. */
+ ++c->_m_defined;
+ assert (was_dangling);
+ prepare_circularity (c);
+ finish_circularity (c);
}
}
- inline void resolve_pending (copier *c)
+ /* Update everything using us to indicate we are no longer dangling.
+ Hereafter, all entries along the reference chain from us should
+ be accounted as pending but not dangling. */
+ inline void prepare_circularity (copier *c)
+ {
+ dump (true) << " resolve refs...\n";
+ std::for_each (_m_patch.begin (), _m_patch.end (), resolve_ref (c));
+ dump (true, true) << " resolve parents...\n";
+ _m_pending->parents_resolve_dangling (c);
+ dump (false, true) << " done with "
+ << _m_pending->_m_pending_count
+ << " pending\n";
+ }
+
+ /* We are now pending but not dangling. This can only mean we are
+ the root of a circular chain of references.
+ */
+ inline void finish_circularity (copier *)
+ {
+ dump () << " circularity FIXME\n";
+ }
+
+ inline void resolve_pending (copier *c, bool was_dangling)
{
- if (_m_pending->resolve_pending ())
+ if (_m_pending->resolve_pending (was_dangling))
// We no longer have any pending references or children!
- finish_pending (c);
+ finish_pending (c, was_dangling);
+ else if (was_dangling && !_m_pending->dangling ())
+ // We've moved up from dangling to pending.
+ promote_pending (c, false, was_dangling);
+ else
+ dump () << " still pending "
+ << (was_dangling ? "(was dangling) " : "")
+ << _m_pending->_m_dangling_count << "/"
+ << _m_pending->_m_pending_count << "\n";
}
struct resolve_ref
: public std::unary_function<std::pair<seen *,
- const value_dispatch **> &,
+ const value_dispatch **>,
void>
{
copier *_m_copier;
inline resolve_ref (copier *c) : _m_copier (c) {}
inline void
- operator () (std::pair<seen *, const value_dispatch **> &p) const
+ operator () (const std::pair<seen *, const value_dispatch **> &p) const
{
p.first->resolve_dangling (_m_copier, false);
}
};
- // add_entry has just created the output DIE we'll refer to.
- inline bool made (copier *c)
- {
- if (_m_pending->complete ())
- {
- // It's all done. Finish up all our references.
- finish_pending (c);
- return true;
- }
-
- // It's still pending, but no longer dangling. Adjust bookkeeping.
- std::for_each (_m_patch.begin (), _m_patch.end (), resolve_ref (c));
- return false;
- }
-
// Our pending_entry is complete. Resolve all pointers to us.
- inline void finish_pending (copier *c)
+ inline void finish_pending (copier *c, bool was_dangling)
{
+ if (was_dangling)
+ ++c->_m_defined;
+
// Create it in the collector.
_m_final = _m_pending->final (c->_m_collector);
+ dump (true) << " final " << _m_final->to_string ()
+ << " resolving parents...\n";
+
/* Tell each parent pending_entry whose children vector points
to us. When we're the last unfinished child, this will
recursively finish the pending parent too. */
- _m_pending->resolve_parents (c);
+ _m_pending->resolve_parents (c, was_dangling);
// No more pending_entry required!
delete _m_pending;
_m_pending = NULL;
+ dump (true, true) << " final resolving refs...\n";
+
/* Fix up each reference attribute pointing to us. When we're
the last dangling reference, this will recursively finish
the referrer pending_entry too. */
@@ -1268,14 +1375,31 @@ namespace elfutils
referrer->resolve_dangling (c, true);
_m_patch.pop_front ();
}
+
+ dump (false, true) << " final done\n";
}
+ static inline void
+ dump_ref (const std::pair<seen *, const value_dispatch **> &p)
+ {
+ std::cout << " " << std::hex << p.first->_m_offset << std::dec;
+ }
+
+ inline void dump_refs () const
+ {
+ if (_m_patch.empty ())
+ return;
+ dump () << " refs:";
+ std::for_each (_m_patch.begin (), _m_patch.end (), dump_ref);
+ std::cout << "\n";
+ }
+
+#if 0
void dump (copier *) const
{
std::cout << _m_pending->_m_patch.size () << " backptrs\n";
}
-#if 0
// This entry is being baked, so update all pointers to us.
inline void resolve (children_copier *c, const debug_info_entry *die)
{
@@ -1353,11 +1477,8 @@ namespace elfutils
{
assert (_m_in->_m_building == this);
_m_in->_m_building = NULL;
-
- if (_m_in->_m_pending == NULL)
+ if (unlikely (_m_out != NULL)) // Exception unwind case only.
delete _m_out;
- else
- assert (_m_in->_m_pending == _m_out);
}
/* Populate _m_out from the corresponding input DIE.
@@ -1384,16 +1505,25 @@ namespace elfutils
throw;
}
+ _m_out = NULL;
+
+ /* Resolve the phantom that stands for references yet to be added.
+ We've added everything now, so we can complete this entry if it
+ doesn't own any dangling references. */
+ _m_in->resolve_dangling (_m_copier, true);
}
/* Complain if we still have dangling references.
If not, it should be impossible to have pending entries left. */
inline void demand_complete () const
{
- if (_m_out->dangling ())
+ assert (_m_out == NULL);
+ assert (_m_in->_m_pending != NULL);
+ if (_m_in->_m_pending->dangling ())
throw std::runtime_error
("compile_unit contains dangling reference attributes");
- assert (_m_out->complete ());
+ assert (_m_copier->_m_defined == _m_copier->_m_seen.size ());
+ assert (_m_in->_m_pending->complete ());
}
// We're adding a reference attribute inside populate, above.
@@ -1412,11 +1542,11 @@ namespace elfutils
/* If the input used DW_TAG_imported_unit, then the logical walk
can hit the same DIE twice. If so, we short-circuit right here. */
+ if (child->_m_final == NULL && child->_m_pending == NULL)
+ make_child (child, in);
+
if (child->_m_final == NULL)
{
- if (child->_m_pending == NULL)
- make_child (child, in);
-
/* Record a back-pointer to this parent entry,
and count its new child as pending. */
child->_m_pending->add_parent (_m_in);
@@ -1428,16 +1558,11 @@ namespace elfutils
Recurse on a new entry_copier object to create it. */
inline void make_child (seen *child, const input_die_ptr &in)
{
- {
- // typename tracker::die2 at (); // XXX
- // typename tracker::step step (_m_copier->_m_tracker, in, at);
+ // typename tracker::die2 at (); // XXX
+ // typename tracker::step step (_m_copier->_m_tracker, in, at);
- entry_copier maker (_m_copier, _m_depth + 1, child, *in);
- maker.populate (*in);
- }
-
- // Fix up dangling references to this entry now that we have it.
- child->made (_m_copier);
+ entry_copier maker (_m_copier, _m_depth + 1, child, *in);
+ maker.populate (*in);
}
// Use "c ()" as a shorthand to get the copier out of the entry_copier.
@@ -1458,6 +1583,7 @@ namespace elfutils
entry_copier maker (this, 0, enter_seen (*in), *in);
maker.populate (*in);
+ dump_seen ();
maker.demand_complete ();
}
@@ -1494,6 +1620,16 @@ namespace elfutils
return die;
}
+ static inline void dump_one_seen (const typename seen_map::value_type &v)
+ {
+ v.second.dump_pending ();
+ }
+
+ inline void dump_seen () const
+ {
+ std::for_each (_m_seen.begin (), _m_seen.end (), dump_one_seen);
+ }
+
inline copier ()
: _m_collector (NULL), _m_tracker (NULL),
_m_seen (), _m_defined (0)