compilation-bundle/dwarf-compilation.base/contrib/libdwarfpp/include/dwarfpp/root-inl.hpp

459 lines
16 KiB
C++

/* dwarfpp: C++ binding for a useful subset of libdwarf, plus extra goodies.
*
* root-inl.hpp: inlines declared in root.hpp.
*
* Copyright (c) 2008--17, Stephen Kell. For licensing information, see the
* LICENSE file in the root of the libdwarfpp tree.
*/
#ifndef DWARFPP_ROOT_INL_HPP_
#define DWARFPP_ROOT_INL_HPP_
#include <iostream>
#include <utility>
#include <set>
#include "root.hpp"
#include "iter.hpp"
#include "dies.hpp"
namespace dwarf
{
using std::string;
using std::set;
using dwarf::core::with_named_children_die;
namespace core
{
/* root_die's name resolution functions */
template <typename Iter>
inline void
root_die::resolve_all(const iterator_base& start, Iter path_pos, Iter path_end,
std::vector<iterator_base >& results, unsigned max /*= 0*/)
{
if (path_pos == path_end)
{ results.push_back(start); /* out of names, so unconditional */ return; }
Iter cur_plus_one = path_pos; cur_plus_one++;
if (cur_plus_one == path_end)
{
auto c = start.named_child(*path_pos);
if (c) { results.push_back(c); if (max != 0 && results.size() >= max) return; }
}
else
{
auto found = start.named_child(*path_pos);
if (found == iterator_base::END) return;
resolve_all(found, ++path_pos, path_end, results, max);
}
}
template <typename Iter>
inline iterator_base
root_die::resolve(const iterator_base& start, Iter path_pos, Iter path_end)
{
std::vector<iterator_base > results;
resolve_all(start, path_pos, path_end, results, 1);
if (results.size() > 0) return *results.begin();
else return iterator_base::END;
}
template <typename Iter>
inline void
root_die::resolve_all_visible_from_root(Iter path_pos, Iter path_end,
std::vector<iterator_base >& results, unsigned max /*= 0*/)
{
if (path_pos == path_end) return;
/* We want to be able to iterate over grandchildren s.t.
*
* - we hit the cached-visible ones first
* - we hit them all eventually
* - we only hit each one once.
*/
set<Dwarf_Off> hit_in_cache;
Iter cur_plus_one = path_pos; cur_plus_one++;
auto recurse = [this, &results, path_end, max, cur_plus_one](const iterator_base& i) {
/* It's visible; use resolve_all from hereon. */
resolve_all(i, cur_plus_one, path_end, results, max);
};
auto matching_cached = visible_named_grandchildren_cache.equal_range(*path_pos);
for (auto i_cached = matching_cached.first;
i_cached != matching_cached.second;
++i_cached)
{
hit_in_cache.insert(i_cached->second);
recurse(pos(i_cached->second, 2));
if (max != 0 && results.size() >= max) return;
}
/* Now we have to be exhaustive. But don't bother if we know that
* our cache is exhaustive. */
if (!visible_named_grandchildren_is_complete)
{
auto vg_seq = visible_named_grandchildren();
for (auto i_g = std::move(vg_seq.first); i_g != vg_seq.second; ++i_g)
{
// skip any with the wrong name.
if (!i_g.name_here() || *i_g.name_here() != *path_pos) continue;
/* skip any we saw before. */
if (hit_in_cache.find(i_g.offset_here()) != hit_in_cache.end()) continue;
/* It's visible; use resolve_all from hereon. */
recurse(i_g);
if (max != 0 && results.size() >= max) return;
}
}
}
inline iterator_base
root_die::resolve(const iterator_base& start, const std::string& name)
{
std::vector<string> path; path.push_back(name);
return resolve(start, path.begin(), path.end());
}
template <typename Iter>
inline iterator_base
root_die::scoped_resolve(const iterator_base& start, Iter path_pos, Iter path_end)
{
std::vector<iterator_base > results = {};
scoped_resolve_all(start, path_pos, path_end, results, 1);
if (results.size() > 0) return *results.begin();
else return iterator_base::END;
}
template <typename Iter>
inline void
root_die::scoped_resolve_all(const iterator_base& start, Iter path_pos, Iter path_end,
std::vector<iterator_base >& results, unsigned max /*= 0*/)
{
if (max != 0 && results.size() >= max) return;
auto found_from_here = resolve(start, path_pos, path_end);
if (found_from_here)
{
results.push_back(found_from_here);
if (max != 0 && results.size() >= max) return;
}
// find our nearest encloser that has named children, and tail-recurse
auto p_encl = start;
do
{
this->move_to_parent(p_encl);
if (p_encl.tag_here() == 0)
{
// we ran out of parents; try visible things in other CUs, then give up
resolve_all_visible_from_root(path_pos, path_end, results, max);
return;
}
} while (!p_encl.is_a<with_named_children_die>());
// successfully moved to an encloser; tail-call to continue resolving
scoped_resolve_all(p_encl, path_pos, path_end, results, max);
// by definition, we're finished
}
template <typename Iter/* = iterator_df<compile_unit_die>*/ >
inline Iter root_die::enclosing_cu(const iterator_base& it)
{ return cu_pos<Iter>(it.get_enclosing_cu_offset()); }
//// FIXME: do I need this function?
//inline iterator_base root_die::scoped_resolve(const std::string& name)
//{ return scoped_resolve(this->begin(), name); }
inline
dwarf::core::sequence<
typename subseq_t<iterator_sibs<>, is_a_t<compile_unit_die> >::transformed_iterator
>
root_die::children() const
{
return begin().children().subseq_of<compile_unit_die>();
}
inline
dwarf::core::sequence<root_die::grandchildren_iterator>
root_die::grandchildren() const
{
auto p_seq = std::make_shared<srk31::concatenating_sequence< iterator_sibs<> > >();
auto cu_seq = children();
for (auto i_cu = std::move(cu_seq.first); i_cu != cu_seq.second; ++i_cu)
{
pair<iterator_sibs<>, iterator_sibs<> > children_seq = i_cu.base().children_here();
p_seq->append(std::move(children_seq.first), std::move(children_seq.second));
}
return make_pair(p_seq->begin(), p_seq->end());
}
inline
dwarf::core::sequence<root_die::visible_named_grandchildren_iterator>
root_die::visible_named_grandchildren() const
{
auto g_seq = this->grandchildren();
return make_pair(
root_die::visible_named_grandchildren_iterator(
g_seq.first, g_seq.second
),
root_die::visible_named_grandchildren_iterator(
g_seq.second, g_seq.second
)
);
}
// inline
// pair<
// root_die::visible_grandchildren_with_name_iterator,
// root_die::visible_grandchildren_with_name_iterator
// >
// root_die::visible_grandchildren_with_name(const string& name) const
// {
// /* ensure the cache is full */
// if (!visible_named_grandchildren_is_complete)
// {
// auto vg_seq = this->visible_named_grandchildren();
// for (auto i = vg_seq.first; i != vg_seq.second; ++i);
// }
// assert(visible_named_grandchildren_is_complete);
// /* now just use the cache */
// auto vg_seq = this->visible_named_grandchildren();
// auto range = this->visible_named_grandchildren_cache.equal_range(name);
// return make_pair(
// root_die::visible_grandchildren_with_name_iterator(
// range.first, grandchild_die_at_offset(*this)
// ),
// root_die::visible_grandchildren_with_name_iterator(
// range.second, grandchild_die_at_offset(&this)
// )
// );
// }
/* NOTE: pos() is incompatible with a strict parent cache.
* But it is necessary to support following references.
* We fill in the parent if depth <= 2, or if the user can tell us. */
template <typename Iter /* = iterator_df<> */ >
inline Iter root_die::pos(Dwarf_Off off, opt<unsigned short> opt_depth /* opt<unsigned short>() */,
opt<Dwarf_Off> parent_off /* = opt<Dwarf_Off>() */,
opt<pair<Dwarf_Off, Dwarf_Half> > referencer /* = opt<pair<Dwarf_Off, Dwarf_Half> >() */ )
{
if (opt_depth && *opt_depth == 0) { assert(off == 0UL); assert(!referencer); return Iter(begin()); }
// always check the live set first
auto found = live_dies.find(off);
if (found != live_dies.end())
{
// it's there, so use find_upwards to get the iterator
return iterator_base(*found->second);
}
Die h(*this, off);
assert(h.handle.get());
iterator_base base(std::move(h), opt_depth, *this);
if (opt_depth && *opt_depth == 1) parent_of[off] = 0UL;
else if (opt_depth && *opt_depth == 2) parent_of[off] = base.enclosing_cu_offset_here();
else if (parent_off) parent_of[off] = *parent_off;
// do we know anything about the first_child_of and next_sibling_of?
// NO because we don't know where we are w.r.t. other siblings
if (base && referencer) refers_to[*referencer] = base.offset_here();
return Iter(std::move(base));
}
template <typename Iter /* = iterator_df<> */ >
inline Iter root_die::find_upwards(Dwarf_Off off, root_die::ptr_type maybe_ptr)
{
/* Use the parent cache to verify our existence and
* get our depth. */
int height = 0; // we're at least at depth 0; will increment every time we go up successfully
Dwarf_Off cur = off;
//map<Dwarf_Off, Dwarf_Off>::iterator i_found_parent;
//do
//{
// i_found_parent = parent_of.find(cur);
// ++height;
//} while (i_found_parent != parent_of.end() && (cur = i_found_parent->second, true));
/*
What we want is
- to search all the way to the top
- when we hit offset 0, `height' should be the depth of `off'
*/
for (auto i_found_parent = parent_of.find(cur);
cur != 0 && i_found_parent != parent_of.end();
i_found_parent = parent_of.find(cur))
{
cur = i_found_parent->second;
++height;
}
// if we got all the way to the root, cur will be 0
if (cur == 0)
{
// CARE: this recursion is safe because pos never calls back to us
// with a non-null maybe_ptr
if (!maybe_ptr) return pos(off, height, parent_of[off]);
else return iterator_base(*maybe_ptr, opt<unsigned short>(height));
}
else
{
debug() << "Did not find DIE at offset 0x" << std::hex << off << std::dec << std::endl;
return iterator_base::END;
}
}
template <typename Iter /* = iterator_df<> */ >
inline Iter root_die::find(Dwarf_Off off,
opt<pair<Dwarf_Off, Dwarf_Half> > referencer /* = opt<pair<Dwarf_Off, Dwarf_Half> >() */,
root_die::ptr_type maybe_ptr /* = root_die::ptr_type(nullptr) */)
{
Iter found_up = find_upwards(off, maybe_ptr);
if (found_up != iterator_base::END)
{
if (referencer) refers_to[*referencer] = found_up.offset_here();
return found_up;
}
else
{
auto found = find_downwards(off);
if (found && referencer) refers_to[*referencer] = found.offset_here();
return found;
}
}
/* We use the properties of DIE trees to avoid a naive depth-first search.
* FIXME: make it work with encap::-style less strict ordering.
* NOTE: a possible idea here is to support a kind of "fractional offsets"
* where we borrow *high-order* bits from the offset space in a dynamic
* fashion. We need some per-root bookkeeping about what offsets have
* been issued, and a way to get a numerical comparison (for search
* functions, like this one).
* Probably the best way to accommodate this is as a new class
* used in place of Dwarf_Off. */
template <typename Iter /* = iterator_df<> */ >
inline Iter root_die::find_downwards(Dwarf_Off off)
{
/* Interesting problem: our iterators don't make searching a subtree
* easy. I think there is a neat way of expressing this by combining
* dfs and bfs traversal. FIXME: work out the recipe. */
/* I think we want bf traversal with a smart subtree-skipping test. */
iterator_bf<typename Iter::DerefType> pos = begin();
// debug(2) << "Searching for offset " << std::hex << off << std::dec << endl;
// debug(2) << "Beginning search at 0x" << std::hex << pos.offset_here() << std::dec << endl;
while (pos != iterator_base::END && pos.offset_here() != off)
{
/* What's next in the breadth-first order? */
assert(((void)pos.offset_here(), true));
// debug(2) << "Began loop body; pos is 0x"
// << std::hex << pos.offset_here() << std::dec;
iterator_bf<typename Iter::DerefType> next_pos = pos;
assert(((void)pos.offset_here(), true));
next_pos.increment();
assert(((void)pos.offset_here(), true));
// debug(2) << ", next_pos is ";
// if (next_pos != iterator_base::END) {
// debug(2) << std::hex << next_pos.offset_here() << std::dec;
//} else debug(2) << "(END)";
// debug(2) << endl;
/* Does the move pos->next_pos skip over (enqueue) a subtree?
* If so, the depth will stay the same.
* If no, it's because we took a previously enqueued (deeper, but earlier)
* node out of the queue (i.e. descended instead of skipped-over). */
if (next_pos != iterator_base::END && next_pos.depth() == pos.depth())
{
// debug(2) << "next_pos is at same depth..." << endl;
// if I understand correctly....
assert(next_pos.offset_here() > pos.offset_here());
/* Might that subtree contain off? */
if (off < next_pos.offset_here() && off > pos.offset_here())
{
// debug(2) << "We think that target is in subtree ..." << endl;
/* Yes. We want that subtree.
* We don't want to move_to_first_child,
* because that will put the bfs traversal in a weird state
* (s.t. next_pos might take us *upwards* not just across/down).
* But we don't want to increment through everything,
* because that will be slow.
* Instead,
* - create a new bf iterator at pos (with empty queue);
* - increment it once normally, so that the subtree is enqueued;
* - continue the loop. */
iterator_bf<typename Iter::DerefType> new_pos
= static_cast<iterator_base>(pos);
new_pos.increment();
if (new_pos != iterator_base::END) {
// previously I had the following slow code:
// //do { pos.increment(); } while (pos.offset_here() > off);
pos = new_pos;
// debug(2) << "Fast-forwarded pos to "
// << std::hex << pos.offset_here() << std::dec << std::endl;
continue;
}
else
{
// subtree is empty -- we have failed
pos = iterator_base::END;
continue;
}
}
else // off >= next_pos.offset_here() || off <= pos.offset_here()
{
// debug(2) << "Subtree between pos and next_pos cannot possibly contain target..." << endl;
/* We can't possibly want that subtree. */
pos.increment_skipping_subtree();
continue;
}
}
else
{
// next is END, or is at a different (lower) depth than pos
pos.increment();
continue;
}
assert(false); // i.e. the above cases must cover everything
}
// debug(2) << "Search returning ";
// if (pos == iterator_base::END) debug(2) << "(END)";
// else debug(2) << std::hex << pos.offset_here() << std::dec;
// debug(2) << endl;
return pos;
}
// FIXME: what does this constructor do? Can we get rid of it?
// It seems to be used only for the compile_unit_die constructor.
//inline basic_die::basic_die(root_die& r/*, const Iter& i*/)
//: d(Die::handle_type(nullptr)), p_root(&r) {}
template <typename Iter /* = iterator_df<> */ >
inline Iter root_die::begin()
{
/* The first DIE is always the root.
* We denote an iterator pointing at the root by
* a Debug but no Die. */
iterator_base base(*this);
assert(base.is_root_position());
Iter it(base);
assert(it.is_root_position());
return it;
}
template <typename Iter /* = iterator_df<> */ >
inline Iter root_die::end()
{
return Iter(iterator_base::END);
}
template <typename Iter /* = iterator_df<> */ >
inline pair<Iter, Iter> root_die::sequence()
{
return std::make_pair(begin(), end());
}
}
}
#endif