blob: ab1755e9f38ee01dd0bc9bb68f1ac015ceccc607 [file] [log] [blame]
// 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