blob: a4063194d12e86805f684f95d84c44e7e249d8e1 [file] [log] [blame]
// Copyright 2018 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/module_symbol_index_node.h"
#include "gtest/gtest.h"
#include "llvm/DebugInfo/DWARF/DWARFDie.h"
namespace zxdb {
using DieRef = ModuleSymbolIndexNode::DieRef;
using RefType = ModuleSymbolIndexNode::RefType;
// Tests de-duplicating type definitions and namespaces, and upgrading forward
// declarations to full definitions.
TEST(ModuleSymbolIndexNode, DeDupe) {
ModuleSymbolIndexNode node;
// Add a function.
const uint32_t kFunction1Offset = 10;
node.AddDie(DieRef(RefType::kFunction, kFunction1Offset));
// Type forward declaration should get appended.
const uint32_t kFwdDecl1Offset = 20;
node.AddDie(DieRef(RefType::kTypeDecl, kFwdDecl1Offset));
ASSERT_EQ(2u, node.dies().size());
EXPECT_EQ(kFunction1Offset, node.dies()[0].offset());
EXPECT_EQ(kFwdDecl1Offset, node.dies()[1].offset());
// Another forward declaration should be ignored in favor of the old one.
const uint32_t kFwdDecl2Offset = 30;
node.AddDie(DieRef(RefType::kTypeDecl, kFwdDecl2Offset));
ASSERT_EQ(2u, node.dies().size());
EXPECT_EQ(kFunction1Offset, node.dies()[0].offset());
EXPECT_EQ(kFwdDecl1Offset, node.dies()[1].offset());
// A full type definition should overwrite the forward declaration.
const uint32_t kType1Offset = 40;
node.AddDie(DieRef(RefType::kType, kType1Offset));
ASSERT_EQ(2u, node.dies().size());
EXPECT_EQ(kFunction1Offset, node.dies()[0].offset());
EXPECT_EQ(kType1Offset, node.dies()[1].offset());
// A duplicate full type definition should be ignored in favor of the old
// one.
const uint32_t kType2Offset = 50;
node.AddDie(DieRef(RefType::kType, kType2Offset));
ASSERT_EQ(2u, node.dies().size());
EXPECT_EQ(kFunction1Offset, node.dies()[0].offset());
EXPECT_EQ(kType1Offset, node.dies()[1].offset());
// Add a namespace, it should be appended.
const uint32_t kNS1Offset = 60;
node.AddDie(DieRef(RefType::kNamespace, kNS1Offset));
ASSERT_EQ(3u, node.dies().size());
EXPECT_EQ(kFunction1Offset, node.dies()[0].offset());
EXPECT_EQ(kType1Offset, node.dies()[1].offset());
EXPECT_EQ(kNS1Offset, node.dies()[2].offset());
// A duplicate namespace should be ignored in favor of the old one.
const uint32_t kNS2Offset = 70;
node.AddDie(DieRef(RefType::kNamespace, kNS2Offset));
ASSERT_EQ(3u, node.dies().size());
EXPECT_EQ(kFunction1Offset, node.dies()[0].offset());
EXPECT_EQ(kType1Offset, node.dies()[1].offset());
EXPECT_EQ(kNS1Offset, node.dies()[2].offset());
// A variable should be appended.
const uint32_t kVar1Offset = 80;
node.AddDie(DieRef(RefType::kVariable, kVar1Offset));
ASSERT_EQ(4u, node.dies().size());
EXPECT_EQ(kFunction1Offset, node.dies()[0].offset());
EXPECT_EQ(kType1Offset, node.dies()[1].offset());
EXPECT_EQ(kNS1Offset, node.dies()[2].offset());
EXPECT_EQ(kVar1Offset, node.dies()[3].offset());
// Duplicate function and variable should be appended.
const uint32_t kFunction2Offset = 90;
const uint32_t kVar2Offset = 100;
node.AddDie(DieRef(RefType::kFunction, kFunction2Offset));
node.AddDie(DieRef(RefType::kVariable, kVar2Offset));
ASSERT_EQ(6u, node.dies().size());
EXPECT_EQ(kFunction1Offset, node.dies()[0].offset());
EXPECT_EQ(kType1Offset, node.dies()[1].offset());
EXPECT_EQ(kNS1Offset, node.dies()[2].offset());
EXPECT_EQ(kVar1Offset, node.dies()[3].offset());
EXPECT_EQ(kFunction2Offset, node.dies()[4].offset());
EXPECT_EQ(kVar2Offset, node.dies()[5].offset());
}
// Tests AddChild() and its merging capabilities when a duplicate is found.
TEST(ModuleSymbolIndexNode, AddChildMerge) {
const uint32_t offset1 = 10;
const uint32_t offset2 = 20;
const uint32_t offset3 = 30;
const uint32_t offset4 = 40;
const std::string foo("foo");
const std::string bar("bar");
const std::string bloop("bloop");
// The root has the hierarchy:
// [root]
// node1 = "foo" [1 function = #1]
// node2 = "bar" [1 function = #2]
ModuleSymbolIndexNode node2;
node2.AddDie(DieRef(RefType::kFunction, offset2));
ModuleSymbolIndexNode node1;
node1.AddDie(DieRef(RefType::kFunction, offset1));
node1.AddChild(bar, std::move(node2));
ModuleSymbolIndexNode root;
EXPECT_TRUE(root.empty());
root.AddChild(foo, std::move(node1));
EXPECT_FALSE(root.empty());
// The merged one has the hierarchy:
// merge1 = "foo" [1 function = #3]
// merge2 = "bloop" [1 function = #4]
ModuleSymbolIndexNode merge2;
merge2.AddDie(DieRef(RefType::kFunction, offset4));
ModuleSymbolIndexNode merge1;
merge1.AddDie(DieRef(RefType::kFunction, offset3));
merge1.AddChild(bloop, std::move(merge2));
// Now merge in "merge1" as a child of the root.
root.AddChild(foo, std::move(merge1));
// This should merge the two to get:
// [root]
// out1 = "foo" [2 functions = #1, #3]
// out2 = "bar" [1 function = #2]
// out3 = "bloop" [1 function = #4]
// Check root.
ASSERT_EQ(1u, root.sub().size());
EXPECT_FALSE(root.empty());
EXPECT_TRUE(root.dies().empty());
EXPECT_EQ(foo, root.sub().begin()->first);
// Check out1.
const ModuleSymbolIndexNode& out1 = root.sub().begin()->second;
ASSERT_EQ(2u, out1.dies().size());
EXPECT_EQ(offset1, out1.dies()[0].offset());
EXPECT_EQ(offset3, out1.dies()[1].offset());
ASSERT_EQ(2u, out1.sub().size());
EXPECT_EQ(bar, out1.sub().begin()->first);
EXPECT_EQ(bloop, (++out1.sub().begin())->first);
// Check out2.
const ModuleSymbolIndexNode& out2 = out1.sub().begin()->second;
EXPECT_TRUE(out2.sub().empty());
ASSERT_EQ(1u, out2.dies().size());
EXPECT_EQ(offset2, out2.dies()[0].offset());
// Check out3.
const ModuleSymbolIndexNode& out3 = (++out1.sub().begin())->second;
EXPECT_TRUE(out3.sub().empty());
ASSERT_EQ(1u, out3.dies().size());
EXPECT_EQ(offset4, out3.dies()[0].offset());
}
} // namespace zxdb