| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/developer/debug/zxdb/symbols/index_node.h" |
| |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include <sstream> |
| |
| #include "src/developer/debug/zxdb/symbols/function.h" |
| #include "src/developer/debug/zxdb/symbols/symbol_factory.h" |
| |
| namespace zxdb { |
| |
| namespace { |
| |
| void DumpMap(const IndexNode::Map& map, int indent, const char* heading, |
| const SymbolFactory* factory_for_loc, std::ostream& out) { |
| if (map.empty()) |
| return; |
| |
| out << std::string(indent * 2, ' ') << heading << std::endl; |
| for (const auto& cur : map) |
| cur.second.Dump(cur.first, out, factory_for_loc, indent + 1); |
| } |
| |
| } // namespace |
| |
| IndexNode* IndexNode::AddChild(Kind kind, std::string_view name) { |
| FX_DCHECK(name.data()); |
| |
| // TODO(brettw) Get some kind of transparent lookup here to avoid making an intermediate |
| // std::string. |
| Map& map = MapForKind(kind); |
| auto found = map.find(name.data()); |
| if (found == map.end()) { |
| found = map.emplace(std::piecewise_construct, std::forward_as_tuple(name), |
| std::forward_as_tuple(kind)) |
| .first; |
| } |
| |
| return &found->second; |
| } |
| |
| IndexNode* IndexNode::AddChild(Kind kind, std::string_view name, const SymbolRef& ref) { |
| auto added = AddChild(kind, name); |
| added->AddDie(ref); |
| return added; |
| } |
| |
| void IndexNode::AddDie(const SymbolRef& ref) { |
| switch (kind_) { |
| case Kind::kNone: |
| case Kind::kRoot: |
| case Kind::kTemplateParameter: |
| FX_NOTREACHED() << "Should not try to add a none, root, or template param DIE."; |
| return; |
| case Kind::kNamespace: |
| // Don't bother saving namespaces. |
| return; |
| case Kind::kType: |
| // A type can only have one entry. If it's a forward declaration, we'll promote it to a |
| // definition. But otherwise won't append. |
| if (!dies_.empty()) { |
| if (!dies_[0].is_declaration()) |
| return; // Existing one is already a definition, never need another. |
| else if (ref.is_declaration()) |
| return; // Both existing one and new one are definitions, don't need to upgrade. |
| dies_.clear(); // Update existing one by removing, will be appended below. |
| } |
| break; |
| case Kind::kFunction: |
| case Kind::kVar: |
| break; // Always store these kinds. |
| } |
| |
| dies_.push_back(ref); |
| } |
| |
| const IndexNode::Map& IndexNode::MapForKind(Kind kind) const { |
| FX_DCHECK(static_cast<int>(kind) >= 0 && |
| static_cast<int>(kind) < static_cast<int>(Kind::kEndPhysical)); |
| return children_[static_cast<int>(kind)]; |
| } |
| |
| IndexNode::Map& IndexNode::MapForKind(Kind kind) { |
| FX_DCHECK(static_cast<int>(kind) >= 0 && |
| static_cast<int>(kind) < static_cast<int>(Kind::kEndPhysical)); |
| return children_[static_cast<int>(kind)]; |
| } |
| |
| void IndexNode::MergeFrom(IndexNode& from) { |
| // Append the DIEs directly on this node. |
| dies_.insert(dies_.end(), from.dies_.begin(), from.dies_.end()); |
| |
| // Merge each sub-type. |
| MergeFromKind(Kind::kNamespace, from); |
| MergeFromKind(Kind::kType, from); |
| MergeFromKind(Kind::kFunction, from); |
| MergeFromKind(Kind::kVar, from); |
| } |
| |
| std::string IndexNode::AsString(int indent_level) const { |
| std::ostringstream out; |
| Dump(out, nullptr, indent_level); |
| return out.str(); |
| } |
| |
| void IndexNode::Dump(std::ostream& out, const SymbolFactory* factory_for_loc, |
| int indent_level) const { |
| DumpMap(namespaces(), indent_level + 1, "Namespaces:", factory_for_loc, out); |
| DumpMap(types(), indent_level + 1, "Types:", factory_for_loc, out); |
| DumpMap(functions(), indent_level + 1, "Functions:", factory_for_loc, out); |
| DumpMap(vars(), indent_level + 1, "Variables:", factory_for_loc, out); |
| } |
| |
| void IndexNode::Dump(const std::string& name, std::ostream& out, |
| const SymbolFactory* factory_for_loc, int indent_level) const { |
| out << std::string(indent_level * 2, ' '); |
| if (name.empty()) |
| out << "<<empty index string>>"; |
| else |
| out << name; |
| |
| if (factory_for_loc) { |
| // Dump location information too. |
| const char* separator = ": "; |
| for (const SymbolRef& die_ref : dies_) { |
| out << separator; |
| separator = ", "; |
| |
| LazySymbol lazy = factory_for_loc->MakeLazy(die_ref.offset()); |
| const Symbol* symbol = lazy.Get(); |
| if (const Function* function = symbol->As<Function>()) { |
| out << function->code_ranges().ToString(); |
| } else { |
| // Everything else just gets the DIE offset so we can identify it. This can be customized |
| // in the future if needed. |
| out << std::hex << "0x" << die_ref.offset(); |
| } |
| } |
| } |
| |
| out << std::endl; |
| Dump(out, factory_for_loc, indent_level); |
| } |
| |
| void IndexNode::MergeFromKind(Kind kind, IndexNode& from) { |
| Map& from_map = from.MapForKind(kind); |
| Map& dest_map = MapForKind(kind); |
| |
| auto from_iter = from_map.begin(); |
| while (from_iter != from_map.end()) { |
| if (auto dest_found = dest_map.find(from_iter->first); dest_found != dest_map.end()) { |
| // Recursively merge. |
| dest_found->second.MergeFrom(from_iter->second); |
| |
| ++from_iter; |
| } else { |
| // New item, can just take the IndexNode and reparent to us. |
| |
| // Advance to the next item since moving the current one will invalidate the iterator. |
| auto move_iter = from_iter; |
| ++from_iter; |
| |
| dest_map.insert(from_map.extract(move_iter)); |
| } |
| } |
| } |
| |
| } // namespace zxdb |