/* 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 #include #include #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 inline void root_die::resolve_all(const iterator_base& start, Iter path_pos, Iter path_end, std::vector& 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 inline iterator_base root_die::resolve(const iterator_base& start, Iter path_pos, Iter path_end) { std::vector results; resolve_all(start, path_pos, path_end, results, 1); if (results.size() > 0) return *results.begin(); else return iterator_base::END; } template inline void root_die::resolve_all_visible_from_root(Iter path_pos, Iter path_end, std::vector& 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 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 path; path.push_back(name); return resolve(start, path.begin(), path.end()); } template inline iterator_base root_die::scoped_resolve(const iterator_base& start, Iter path_pos, Iter path_end) { std::vector results = {}; scoped_resolve_all(start, path_pos, path_end, results, 1); if (results.size() > 0) return *results.begin(); else return iterator_base::END; } template inline void root_die::scoped_resolve_all(const iterator_base& start, Iter path_pos, Iter path_end, std::vector& 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()); // 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 */ > inline Iter root_die::enclosing_cu(const iterator_base& it) { return cu_pos(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, is_a_t >::transformed_iterator > root_die::children() const { return begin().children().subseq_of(); } inline dwarf::core::sequence root_die::grandchildren() const { auto p_seq = std::make_shared > >(); auto cu_seq = children(); for (auto i_cu = std::move(cu_seq.first); i_cu != cu_seq.second; ++i_cu) { pair, 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() 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 */ > inline Iter root_die::pos(Dwarf_Off off, opt opt_depth /* opt() */, opt parent_off /* = opt() */, opt > referencer /* = opt >() */ ) { 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 */ > 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::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(height)); } else { debug() << "Did not find DIE at offset 0x" << std::hex << off << std::dec << std::endl; return iterator_base::END; } } template */ > inline Iter root_die::find(Dwarf_Off off, opt > referencer /* = opt >() */, 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 */ > 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 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 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 new_pos = static_cast(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 */ > 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 */ > inline Iter root_die::end() { return Iter(iterator_base::END); } template */ > inline pair root_die::sequence() { return std::make_pair(begin(), end()); } } } #endif