459 lines
16 KiB
C++
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
|