blob: 174b43e9da8aa29f07ce22bcb888b93323653ba3 [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/expr/find_name.h"
#include <lib/syslog/cpp/macros.h>
#include <gtest/gtest.h>
#include "llvm/BinaryFormat/Dwarf.h"
#include "src/developer/debug/zxdb/expr/eval_test_support.h"
#include "src/developer/debug/zxdb/expr/expr_parser.h"
#include "src/developer/debug/zxdb/expr/found_name.h"
#include "src/developer/debug/zxdb/symbols/base_type.h"
#include "src/developer/debug/zxdb/symbols/collection.h"
#include "src/developer/debug/zxdb/symbols/elf_symbol.h"
#include "src/developer/debug/zxdb/symbols/function.h"
#include "src/developer/debug/zxdb/symbols/identifier.h"
#include "src/developer/debug/zxdb/symbols/index_test_support.h"
#include "src/developer/debug/zxdb/symbols/mock_module_symbols.h"
#include "src/developer/debug/zxdb/symbols/modified_type.h"
#include "src/developer/debug/zxdb/symbols/namespace.h"
#include "src/developer/debug/zxdb/symbols/process_symbols_test_setup.h"
#include "src/developer/debug/zxdb/symbols/symbol_context.h"
#include "src/developer/debug/zxdb/symbols/symbol_test_parent_setter.h"
#include "src/developer/debug/zxdb/symbols/type_test_support.h"
#include "src/developer/debug/zxdb/symbols/variable_test_support.h"
// NOTE: Finding variables on *this* and subclasses is EvalContextImplTest.FoundThis which tests
// both of our file's finding code as well as the decoding code.
namespace zxdb {
// This test declares the following structure. There are three levels of variables, each one has one
// unique variable, and one labeled "value" for testing ambiguity.
//
// namespace ns {
//
// int32_t ns_value;
//
// void Foo(int32_t value, int32_t other_param) {
// int32_t value; // 2nd declaration.
// int32_t function_local;
// {
// int32_t value; // 3rd declaration.
// int32_t block_local;
// }
// }
//
// } // namespace ns
TEST(FindName, FindLocalVariable) {
ProcessSymbolsTestSetup setup;
MockModuleSymbols* module_symbols = setup.InjectMockModule();
SymbolContext symbol_context(ProcessSymbolsTestSetup::kDefaultLoadAddress);
auto& index_root = module_symbols->index().root();
auto int32_type = MakeInt32Type();
// Empty DWARF location expression. Since we don't evaluate any variables they can all be empty.
DwarfExpr var_loc;
// Set up the module symbols. This creates "ns" and "ns_value" in the symbol index.
const char kNsName[] = "ns";
auto ns_node = index_root.AddChild(IndexNode::Kind::kNamespace, kNsName, IndexNode::SymbolRef());
const char kNsVarName[] = "ns_value";
TestIndexedGlobalVariable ns_value(module_symbols, ns_node, kNsVarName);
// Namespace.
auto ns = fxl::MakeRefCounted<Namespace>();
ns->set_assigned_name(kNsName);
// Function inside the namespace.
auto function = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
function->set_assigned_name("function");
uint64_t kFunctionBeginAddr = ProcessSymbolsTestSetup::kDefaultLoadAddress + 0x1000;
uint64_t kFunctionEndAddr = ProcessSymbolsTestSetup::kDefaultLoadAddress + 0x2000;
function->set_code_ranges(AddressRanges(AddressRange(kFunctionBeginAddr, kFunctionEndAddr)));
SymbolTestParentSetter function_parent(function, ns);
// Function parameters.
auto param_value =
MakeVariableForTest("value", int32_type, kFunctionBeginAddr, kFunctionEndAddr, var_loc);
auto param_other =
MakeVariableForTest("other_param", int32_type, kFunctionBeginAddr, kFunctionEndAddr, var_loc);
function->set_parameters({LazySymbol(param_value), LazySymbol(param_other)});
// Function local variables.
auto var_value =
MakeVariableForTest("value", int32_type, kFunctionBeginAddr, kFunctionEndAddr, var_loc);
auto var_other = MakeVariableForTest("function_local", int32_type, kFunctionBeginAddr,
kFunctionEndAddr, var_loc);
function->set_variables({LazySymbol(var_value), LazySymbol(var_other)});
FindNameContext function_context(&setup.process(), symbol_context, function.get());
// Inner block.
uint64_t kBlockBeginAddr = ProcessSymbolsTestSetup::kDefaultLoadAddress + 0x1100;
uint64_t kBlockEndAddr = ProcessSymbolsTestSetup::kDefaultLoadAddress + 0x1200;
auto block = fxl::MakeRefCounted<CodeBlock>(DwarfTag::kLexicalBlock);
block->set_code_ranges(AddressRanges(AddressRange(kBlockBeginAddr, kBlockEndAddr)));
SymbolTestParentSetter block_parent(block, function);
function->set_inner_blocks({LazySymbol(block)});
// Inner block variables.
auto block_value =
MakeVariableForTest("value", int32_type, kBlockBeginAddr, kBlockEndAddr, var_loc);
auto block_other =
MakeVariableForTest("block_local", int32_type, kBlockBeginAddr, kBlockEndAddr, var_loc);
block->set_variables({LazySymbol(block_value), LazySymbol(block_other)});
FindNameContext block_context(&setup.process(), symbol_context, block.get());
// ACTUAL TEST CODE ------------------------------------------------------------------------------
FindNameOptions all_kinds(FindNameOptions::kAllKinds);
// Find "value" in the nested block should give the block's one.
ParsedIdentifier value_ident(var_value->GetAssignedName());
FoundName found = FindName(block_context, all_kinds, value_ident);
EXPECT_TRUE(found);
EXPECT_EQ(block_value.get(), found.variable());
// Find "value" in the function block should give the function's one.
found = FindName(function_context, all_kinds, value_ident);
EXPECT_TRUE(found);
EXPECT_EQ(var_value.get(), found.variable());
EXPECT_EQ(var_value->GetAssignedName(), found.GetName().GetFullNameNoQual());
// Find "::value" should match nothing.
ParsedIdentifier value_global_ident(IdentifierQualification::kGlobal,
ParsedIdentifierComponent(var_value->GetAssignedName()));
found = FindName(function_context, all_kinds, value_global_ident);
EXPECT_FALSE(found);
// Prefix search for "va" should find all three "values".
std::vector<FoundName> found_vector;
FindNameOptions prefix_options(FindNameOptions::kAllKinds);
prefix_options.how = FindNameOptions::kPrefix;
prefix_options.max_results = 100;
ParsedIdentifier va_identifier("va");
FindLocalVariable(prefix_options, block.get(), va_identifier, &found_vector);
ASSERT_EQ(3u, found_vector.size());
// Limiting the prefix result set to 1 should only fine one.
prefix_options.max_results = 1;
found_vector.clear();
FindLocalVariable(prefix_options, block.get(), va_identifier, &found_vector);
ASSERT_EQ(1u, found_vector.size());
// Find "block_local" in the block should be found, but in the function it should not be.
ParsedIdentifier block_local_ident(block_other->GetAssignedName());
found = FindName(block_context, all_kinds, block_local_ident);
EXPECT_TRUE(found);
EXPECT_EQ(block_other.get(), found.variable());
EXPECT_EQ(block_other->GetAssignedName(), found.GetName().GetFullNameNoQual());
found = FindName(function_context, all_kinds, block_local_ident);
EXPECT_FALSE(found);
// Finding the other function parameter in the block should work.
ParsedIdentifier other_param_ident(param_other->GetAssignedName());
found = FindName(block_context, all_kinds, other_param_ident);
EXPECT_TRUE(found);
EXPECT_EQ(param_other.get(), found.variable());
// Look up the variable "ns::ns_value" using the name "ns_value" (no namespace) from within the
// context of the "ns::function()" function. The namespace of the function should be implicitly
// picked up.
ParsedIdentifier ns_value_ident(kNsVarName);
found = FindName(block_context, all_kinds, ns_value_ident);
EXPECT_TRUE(found);
EXPECT_EQ(ns_value.var.get(), found.variable());
EXPECT_EQ(kNsVarName, found.GetName().GetFullNameNoQual());
// Loop up the global "ns_value" var with no global symbol context. This should fail and not
// crash.
FindNameContext block_no_modules_context;
block_no_modules_context.block = block.get();
found = FindName(block_no_modules_context, all_kinds, ns_value_ident);
EXPECT_FALSE(found);
}
// This test only tests for finding object members. It doesn't set up the index which might find
// types, that's tested by FindIndexedName.
TEST(FindName, FindMember) {
DerivedClassTestSetup d;
FindNameContext context; // Empty context = local and object vars only.
FindNameOptions exact_var(FindNameOptions::kAllKinds);
// The two base classes each have a "b" member.
ParsedIdentifier b_ident("b");
// Finding one member "b" should find the first one (Base1) because the options find the first
// match by default.
std::vector<FoundName> results;
FindMember(context, exact_var, d.derived_type.get(), b_ident, nullptr, &results);
ASSERT_EQ(1u, results.size());
ASSERT_EQ(FoundName::kMemberVariable, results[0].kind());
EXPECT_EQ(d.kBase1Offset, results[0].member().object_path().BaseOffsetInDerived());
EXPECT_EQ("b", results[0].GetName().GetFullNameNoQual());
// Increase the limit, it should find both in order of Base1, Base2.
results.clear();
exact_var.max_results = 100;
FindMember(context, exact_var, d.derived_type.get(), b_ident, nullptr, &results);
ASSERT_EQ(2u, results.size());
ASSERT_EQ(FoundName::kMemberVariable, results[0].kind());
ASSERT_EQ(FoundName::kMemberVariable, results[1].kind());
EXPECT_EQ(d.kBase1Offset, results[0].member().object_path().BaseOffsetInDerived());
EXPECT_EQ(d.kBase2Offset, results[1].member().object_path().BaseOffsetInDerived());
}
// Tests that we implicitly search the object pointer ("this" in C++) for variables without
// qualification.
TEST(FindName, FindMemberOnThis) {
const char kMemberName[] = "member";
auto int_type = MakeInt32Type();
auto class_type = MakeCollectionType(DwarfTag::kClassType, "MyClass", {{kMemberName, int_type}});
// Make lots of qualifiers to test stripping them when evaluating the class. Produce
// "MyClass const * const"
auto const_class_type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kConstType, class_type);
auto ptr_const_class_type =
fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, const_class_type);
auto const_ptr_const_class_type =
fxl::MakeRefCounted<ModifiedType>(DwarfTag::kConstType, ptr_const_class_type);
// This parameter to the function defines the "object pointer". It need not be called "this".
auto param = fxl::MakeRefCounted<Variable>(DwarfTag::kFormalParameter, "self",
const_ptr_const_class_type, VariableLocation());
// Make a member function. It has to have the "object pointer" set.
auto function = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
function->set_object_pointer(param);
// Evaluate the unqualified name in the context of the function.
FindNameContext context;
context.block = function.get();
FindNameOptions options(FindNameOptions::kNoKinds);
options.find_vars = true;
// Do the actual lookup.
FoundName found = FindName(context, options, ParsedIdentifier(kMemberName));
ASSERT_TRUE(found.is_found());
ASSERT_EQ(FoundName::kMemberVariable, found.kind());
EXPECT_EQ(const_ptr_const_class_type.get(), found.object_ptr()->type().Get()->As<Type>());
EXPECT_EQ(class_type->data_members()[0].Get()->As<DataMember>(), found.member().data_member());
}
TEST(FindName, FindAnonUnion) {
// Makes this type:
// struct Outer {
// union Union {
// int inner;
// };
// }
// and makes sure that we can evaluate "outer.inner", transparently going into the anonymous
// union.
auto int_type = MakeInt32Type();
constexpr uint32_t kInnerOffset = 4; // Offset of "inner" inside the union.
constexpr char kInnerName[] = "inner";
auto union_type = MakeCollectionTypeWithOffset(DwarfTag::kUnionType, "", kInnerOffset,
{{kInnerName, int_type}});
constexpr uint32_t kUnionOffset = 2; // Offset of the union inside "Outer".
auto outer_type = MakeCollectionTypeWithOffset(DwarfTag::kStructureType, "Outer", kUnionOffset,
{{"", union_type}});
constexpr uint8_t kIntValue = 42;
ExprValue value(outer_type, {0, 0, 0, 0, 0, 0, // Padding: kInnerOffset + kUnionOffset bytes.
kIntValue, 0, 0, 0}); // 32-bit integer little-endian.
FindNameContext context; // Empty context = local and object vars only.
FindNameOptions exact_var(FindNameOptions::kAllKinds);
std::vector<FoundName> result;
FindMember(context, exact_var, outer_type.get(), ParsedIdentifier(kInnerName), nullptr, &result);
ASSERT_EQ(1u, result.size());
// The found value should be at the correct offset, accounting for both the union and integer
// offsets.
EXPECT_EQ(kInnerOffset + kUnionOffset, result[0].member().GetDataMemberOffset());
EXPECT_EQ(kInnerName, result[0].member().data_member()->GetAssignedName());
}
// This only tests the ModuleSymbols and function naming integration, the details of the index
// searching are tested by FindGlobalNameInModule()
TEST(FindName, FindIndexedName) {
ProcessSymbolsTestSetup setup;
const char kGlobalName[] = "global"; // Different variable in each.
const char kVar1Name[] = "var1"; // Only in module 1
const char kVar2Name[] = "var2"; // Only in module 2
const char kNotFoundName[] = "notfound";
ParsedIdentifier global_ident(kGlobalName);
ParsedIdentifier var1_ident(kVar1Name);
ParsedIdentifier var2_ident(kVar2Name);
ParsedIdentifier notfound_ident(kNotFoundName);
// Module 1.
auto module_symbols1 = fxl::MakeRefCounted<MockModuleSymbols>("mod1.so");
auto& root1 = module_symbols1->index().root(); // Root of the index for module 1.
TestIndexedGlobalVariable global1(module_symbols1.get(), &root1, kGlobalName);
TestIndexedGlobalVariable var1(module_symbols1.get(), &root1, kVar1Name);
constexpr uint64_t kLoadAddress1 = 0x1000;
SymbolContext symbol_context1(kLoadAddress1);
setup.InjectModule("mod1", "1234", kLoadAddress1, module_symbols1);
// Module 2.
auto module_symbols2 = fxl::MakeRefCounted<MockModuleSymbols>("mod2.so");
auto& root2 = module_symbols2->index().root(); // Root of the index for module 1.
TestIndexedGlobalVariable global2(module_symbols2.get(), &root2, kGlobalName);
TestIndexedGlobalVariable var2(module_symbols2.get(), &root2, kVar2Name);
constexpr uint64_t kLoadAddress2 = 0x2000;
SymbolContext symbol_context2(kLoadAddress2);
setup.InjectModule("mod2", "5678", kLoadAddress2, module_symbols2);
FindNameOptions all_opts(FindNameOptions::kAllKinds);
std::vector<FoundName> found;
// Searching for "global" in module1's context should give the global in that module.
FindNameContext mod1_context(&setup.process(), symbol_context1);
FindIndexedName(mod1_context, all_opts, ParsedIdentifier(), global_ident, true, &found);
ASSERT_EQ(1u, found.size());
EXPECT_EQ(global1.var.get(), found[0].variable());
// Searching for "global" in module2's context should give the global in that module.
found.clear();
FindNameContext mod2_context(&setup.process(), symbol_context2);
FindIndexedName(mod2_context, all_opts, ParsedIdentifier(), global_ident, true, &found);
ASSERT_EQ(1u, found.size());
EXPECT_EQ(global2.var.get(), found[0].variable());
// Searching for "var1" in module2's context should still find it even though its in the other
// module.
found.clear();
FindIndexedName(mod2_context, all_opts, ParsedIdentifier(), var1_ident, true, &found);
ASSERT_EQ(1u, found.size());
EXPECT_EQ(var1.var.get(), found[0].variable());
// Searching for "var2" with only target-level symbols should still find it.
found.clear();
FindIndexedName(FindNameContext(&setup.target()), all_opts, ParsedIdentifier(), var2_ident, true,
&found);
ASSERT_EQ(1u, found.size());
EXPECT_EQ(var2.var.get(), found[0].variable());
}
TEST(FindName, FindIndexedNameInModule) {
auto module_symbols = fxl::MakeRefCounted<MockModuleSymbols>("test.so");
auto& index_root = module_symbols->index().root(); // Root of the index.
const char kVarName[] = "var";
const char kNsName[] = "ns";
FindNameOptions all_opts(FindNameOptions::kAllKinds);
std::vector<FoundName> found;
// Make a global variable in the toplevel namespace.
TestIndexedGlobalVariable global(module_symbols.get(), &index_root, kVarName);
ParsedIdentifier var_ident(kVarName);
FindIndexedNameInModule(all_opts, module_symbols.get(), ParsedIdentifier(), var_ident, true,
&found);
ASSERT_EQ(1u, found.size());
EXPECT_EQ(global.var.get(), found[0].variable());
// Say we're in some nested namespace and search for the same name. It should find the variable in
// the upper namespace.
ParsedIdentifier nested_ns(kNsName);
found.clear();
FindIndexedNameInModule(all_opts, module_symbols.get(), nested_ns, var_ident, true, &found);
ASSERT_EQ(1u, found.size());
EXPECT_EQ(global.var.get(), found[0].variable());
// Add a variable in the nested namespace with the same name.
auto ns_node = index_root.AddChild(IndexNode::Kind::kNamespace, kNsName, IndexNode::SymbolRef());
TestIndexedGlobalVariable ns(module_symbols.get(), ns_node, kVarName);
// Re-search for the same name in the nested namespace, it should get the nested one first.
found.clear();
FindIndexedNameInModule(all_opts, module_symbols.get(), nested_ns, var_ident, true, &found);
ASSERT_EQ(1u, found.size());
EXPECT_EQ(ns.var.get(), found[0].variable());
// Now do the same search but globally qualify the input "::var" which should match only the
// toplevel one.
ParsedIdentifier var_global_ident(IdentifierQualification::kGlobal,
ParsedIdentifierComponent(kVarName));
found.clear();
FindIndexedNameInModule(all_opts, module_symbols.get(), nested_ns, var_global_ident, true,
&found);
ASSERT_EQ(1u, found.size());
EXPECT_EQ(global.var.get(), found[0].variable());
EXPECT_EQ(kVarName, found[0].GetName().GetFullNameNoQual());
}
TEST(FindName, FindTypeName) {
ProcessSymbolsTestSetup setup;
MockModuleSymbols* module_symbols = setup.InjectMockModule();
SymbolContext symbol_context(ProcessSymbolsTestSetup::kDefaultLoadAddress);
auto& index_root = module_symbols->index().root();
// Note space in "> >" which is how Clang likes to represent this.
const char kGlobalTypeName[] = "GlobalType<std::char_traits<char> >";
const char kChildTypeName[] = "ChildType<std::char_traits<char> >";
// Global class name.
ParsedIdentifier global_type_name(kGlobalTypeName);
auto global_type = fxl::MakeRefCounted<Collection>(DwarfTag::kClassType);
global_type->set_assigned_name(kGlobalTypeName);
TestIndexedSymbol global_indexed(module_symbols, &index_root, kGlobalTypeName, global_type);
// Child type definition inside the global class name. Currently types don't have child types and
// everything is found via the index.
ParsedIdentifier child_type_name(kChildTypeName);
ParsedIdentifier full_child_type_name;
Err err = ExprParser::ParseIdentifier(
"GlobalType<std::char_traits<char> >::ChildType<std::char_traits<char> >",
&full_child_type_name);
ASSERT_FALSE(err.has_error());
auto child_type = fxl::MakeRefCounted<Collection>(DwarfTag::kClassType);
child_type->set_assigned_name(kChildTypeName);
TestIndexedSymbol child_indexed(module_symbols, global_indexed.index_node, kChildTypeName,
child_type);
// Declares a variable that points to the GlobalType. It will be the "this" pointer for the
// function. The address range of this variable doesn't overlap the function. This means we can
// never compute its value, but since it's syntactically in-scope, we should still be able to use
// its type to resolve type names on the current class.
auto global_type_ptr = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, global_type);
auto this_var =
MakeVariableForTest("this", global_type_ptr, 0x9000, 0x9001,
DwarfExpr({llvm::dwarf::DW_OP_reg0, llvm::dwarf::DW_OP_stack_value}));
// Function as a member of GlobalType.
auto function = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
function->set_assigned_name("function");
uint64_t kFunctionBeginAddr = 0x1000;
uint64_t kFunctionEndAddr = 0x2000;
function->set_code_ranges(AddressRanges(AddressRange(kFunctionBeginAddr, kFunctionEndAddr)));
function->set_object_pointer(this_var);
// This context declares a target and a block but no current module, which means the block and all
// modules should be searched with no particular preference (most other code sets a preference so
// this tests that less common case).
FindNameContext function_context;
function_context.target_symbols = &setup.target();
function_context.block = function.get();
// ACTUAL TEST CODE ------------------------------------------------------------------------------
FindNameOptions all_kinds(FindNameOptions::kAllKinds);
// Look up from the global function.
FoundName found = FindName(function_context, all_kinds, global_type_name);
EXPECT_TRUE(found);
EXPECT_EQ(FoundName::kType, found.kind());
EXPECT_EQ(global_type.get(), found.type().get());
// This has gone through our ParsedIdentifier template canonicalization so doesn't have the
// space between the ">>" like the input had.
EXPECT_EQ("::GlobalType<std::char_traits<char>>", found.GetName().GetFullName());
// Prefix search same as above.
FindNameOptions prefix_opts(FindNameOptions::kAllKinds);
prefix_opts.how = FindNameOptions::kPrefix;
prefix_opts.max_results = 10000;
std::vector<FoundName> found_vect;
ParsedIdentifier global_type_prefix("Gl");
FindName(function_context, prefix_opts, global_type_prefix, &found_vect);
ASSERT_EQ(1u, found_vect.size());
EXPECT_EQ(global_type.get(), found_vect[0].type().get());
// Look up the child function by full name.
found = FindName(function_context, all_kinds, full_child_type_name);
EXPECT_TRUE(found);
EXPECT_EQ(FoundName::kType, found.kind());
EXPECT_EQ(child_type.get(), found.type().get());
// Look up the child function by just the child name. Since the function is a member of
// GlobalType, ChildType is a member of "this" so it should be found.
found = FindName(function_context, all_kinds, child_type_name);
EXPECT_TRUE(found);
EXPECT_EQ(FoundName::kType, found.kind());
EXPECT_EQ(child_type.get(), found.type().get());
}
TEST(FindName, FindTemplateName) {
ProcessSymbolsTestSetup setup;
MockModuleSymbols* module_symbols = setup.InjectMockModule();
SymbolContext symbol_context(ProcessSymbolsTestSetup::kDefaultLoadAddress);
auto& index_root = module_symbols->index().root();
// Declare two functions, one's a template, the other has the same prefix but isn't.
const char kTemplateIntName[] = "Template<int>";
const char kTemplateNotName[] = "TemplateNot";
ParsedIdentifier template_int_name(kTemplateIntName);
ParsedIdentifier template_not_name(kTemplateNotName);
auto template_int = fxl::MakeRefCounted<Collection>(DwarfTag::kClassType);
template_int->set_assigned_name(kTemplateIntName);
TestIndexedSymbol template_int_indexed(module_symbols, &index_root, kTemplateIntName,
template_int);
auto template_not = fxl::MakeRefCounted<Collection>(DwarfTag::kClassType);
template_not->set_assigned_name(kTemplateNotName);
TestIndexedSymbol template_not_indexed(module_symbols, &index_root, kTemplateNotName,
template_not);
// Search for names globally within the target.
FindNameContext context(&setup.target());
FindNameOptions all_types(FindNameOptions::kAllKinds);
// The string "Template" should be identified as one.
ParsedIdentifier template_name("Template");
auto found = FindName(context, all_types, template_name);
EXPECT_TRUE(found);
EXPECT_EQ(FoundName::kTemplate, found.kind());
EXPECT_EQ("Template", found.GetName().GetFullName());
// The string "TemplateNot" is a type, it should be found as such.
std::vector<FoundName> found_vect;
FindName(context, all_types, template_not_name, &found_vect);
ASSERT_EQ(1u, found_vect.size());
EXPECT_EQ(FoundName::kType, found_vect[0].kind());
// Now search only for templates, "TemplateNot" should not be found.
found_vect.clear();
FindNameOptions templates_only(FindNameOptions::kNoKinds);
templates_only.find_templates = true;
FindName(context, templates_only, template_not_name, &found_vect);
EXPECT_TRUE(found_vect.empty());
// Prefix search for "Templ" should get both full types. Since prefix searching doesn't currently
// work for templates, we won't get a template record. These results will need to be updated if
// template prefix matching is added.
found_vect.clear();
FindNameOptions all_prefixes(FindNameOptions::kAllKinds);
all_prefixes.how = FindNameOptions::kPrefix;
all_prefixes.max_results = 100;
ParsedIdentifier templ_name("Templ");
FindName(context, all_prefixes, templ_name, &found_vect);
ASSERT_EQ(2u, found_vect.size());
// Both results are types.
EXPECT_EQ(FoundName::kType, found_vect[0].kind());
EXPECT_EQ(FoundName::kType, found_vect[1].kind());
// Can appear in either order.
EXPECT_TRUE((found_vect[0].type().get() == template_int.get() &&
found_vect[1].type().get() == template_not.get()) ||
(found_vect[0].type().get() == template_not.get() &&
found_vect[1].type().get() == template_int.get()));
}
TEST(FindName, FindType) {
ProcessSymbolsTestSetup setup;
auto module_symbols1 = fxl::MakeRefCounted<MockModuleSymbols>("mod1.so");
auto& index_root1 = module_symbols1->index().root();
auto module_symbols2 = fxl::MakeRefCounted<MockModuleSymbols>("mod2.so");
auto& index_root2 = module_symbols2->index().root();
const char kStructName[] = "Struct";
ParsedIdentifier struct_name(kStructName);
// Make and index the forward declaration in module 1.
auto fwd_decl = fxl::MakeRefCounted<Collection>(DwarfTag::kStructureType);
fwd_decl->set_assigned_name(kStructName);
fwd_decl->set_is_declaration(true);
TestIndexedSymbol fwd_decl_indexed(module_symbols1.get(), &index_root1, kStructName, fwd_decl);
// Make and index a definition in module 2.
auto def = fxl::MakeRefCounted<Collection>(DwarfTag::kClassType);
def->set_assigned_name(kStructName);
def->set_byte_size(12);
TestIndexedSymbol def_indexed(module_symbols2.get(), &index_root2, kStructName, def);
// Set the modules as loaded.
constexpr uint64_t kLoadAddress1 = 0x1000;
SymbolContext symbol_context1(kLoadAddress1);
setup.InjectModule("mod1", "1234", kLoadAddress1, module_symbols1);
constexpr uint64_t kLoadAddress2 = 0x2000;
SymbolContext symbol_context2(kLoadAddress2);
setup.InjectModule("mod2", "5678", kLoadAddress2, module_symbols2);
// Search for names starting from "mod1" so the output ordering is guaranteed.
FindNameContext context(&setup.process(), symbol_context1);
// Finding types should return both the forward definition and the definition.
FindNameOptions find_types(FindNameOptions::kNoKinds);
find_types.find_types = true;
find_types.max_results = 100;
std::vector<FoundName> results;
FindName(context, find_types, struct_name, &results);
ASSERT_EQ(2u, results.size());
// The forward-declaration should be found first since it's in the "current" module we passed to
// FindName.
EXPECT_EQ(fwd_decl.get(), results[0].type().get());
EXPECT_EQ(def.get(), results[1].type().get());
// Now find only definitions.
FindNameOptions find_type_defs(FindNameOptions::kNoKinds);
find_type_defs.find_type_defs = true;
find_type_defs.max_results = 100;
// Should find only the definition now.
results.clear();
FindName(context, find_type_defs, struct_name, &results);
ASSERT_EQ(1u, results.size());
EXPECT_EQ(def.get(), results[0].type().get());
}
TEST(FindName, FindNamespace) {
ProcessSymbolsTestSetup setup;
MockModuleSymbols* module_symbols = setup.InjectMockModule();
auto& index_root = module_symbols->index().root();
SymbolContext symbol_context(ProcessSymbolsTestSetup::kDefaultLoadAddress);
FindNameContext context(&setup.process(), symbol_context);
const char kStd[] = "std";
index_root.AddChild(IndexNode::Kind::kNamespace, kStd);
const char kStar[] = "star";
auto* star_ns = index_root.AddChild(IndexNode::Kind::kNamespace, kStar);
// star::internal
const char kInternal[] = "internal";
star_ns->AddChild(IndexNode::Kind::kNamespace, kInternal);
FindNameOptions find_ns(FindNameOptions::kNoKinds);
find_ns.find_namespaces = true;
find_ns.max_results = 100;
// Find the "std" namespace.
std::vector<FoundName> results;
FindName(context, find_ns, ParsedIdentifier(kStd), &results);
ASSERT_EQ(1u, results.size());
EXPECT_EQ(FoundName::kNamespace, results[0].kind());
EXPECT_EQ(kStd, results[0].GetName().GetFullName());
// Find "s..." namespaces by prefix.
FindNameOptions find_ns_prefix = find_ns;
find_ns_prefix.how = FindNameOptions::kPrefix;
results.clear();
FindName(context, find_ns_prefix, ParsedIdentifier("s"), &results);
ASSERT_EQ(2u, results.size());
// Results can be in either order.
EXPECT_TRUE(
(results[0].GetName().GetFullName() == kStd && results[1].GetName().GetFullName() == kStar) ||
(results[0].GetName().GetFullName() == kStar && results[1].GetName().GetFullName() == kStd));
// Find the "star::i" namespace by prefix.
ParsedIdentifier star_internal_prefix;
ASSERT_TRUE(ExprParser::ParseIdentifier("star::i", &star_internal_prefix).ok());
results.clear();
FindName(context, find_ns_prefix, star_internal_prefix, &results);
ASSERT_EQ(1u, results.size());
EXPECT_EQ("star::internal", results[0].GetName().GetFullName());
}
// A symbol should be found in any namespace with the "all namespaces" flag set.
TEST(FindName, FindRecursiveNamespace) {
ProcessSymbolsTestSetup setup;
MockModuleSymbols* module_symbols = setup.InjectMockModule();
auto& index_root = module_symbols->index().root();
SymbolContext symbol_context(ProcessSymbolsTestSetup::kDefaultLoadAddress);
FindNameContext context(&setup.process(), symbol_context);
// Make several functions
//
// - ::Foo()
// - ::std::Foo()
// - ::std::bar::Foo()
// - ::std::$anon::Foo()
const char kStdName[] = "std";
auto std_ns_symbol = fxl::MakeRefCounted<Namespace>(kStdName);
auto std_ns = index_root.AddChild(IndexNode::Kind::kNamespace, kStdName);
const char kBarName[] = "bar";
auto std_bar_ns_symbol = fxl::MakeRefCounted<Namespace>(kBarName);
SymbolTestParentSetter std_bar_ns_symbol_parent(std_bar_ns_symbol, std_ns_symbol);
auto std_bar_ns = std_ns->AddChild(IndexNode::Kind::kNamespace, kBarName);
auto std_anon_ns_symbol = fxl::MakeRefCounted<Namespace>(std::string());
SymbolTestParentSetter std_anon_ns_symbol_parent(std_anon_ns_symbol, std_ns_symbol);
auto std_anon_ns = std_ns->AddChild(IndexNode::Kind::kNamespace, "");
// ::Foo().
const char kFooName[] = "Foo";
auto foo = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
foo->set_assigned_name(kFooName);
TestIndexedSymbol foo_indexed(module_symbols, &index_root, kFooName, foo);
// ::std::Foo().
auto std_foo = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
SymbolTestParentSetter std_foo_parent(std_foo, std_ns_symbol);
std_foo->set_assigned_name(kFooName);
TestIndexedSymbol std_foo_indexed(module_symbols, std_ns, kFooName, std_foo);
// ::std::bar::Foo().
auto std_bar_foo = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
SymbolTestParentSetter std_bar_foo_parent(std_bar_foo, std_bar_ns_symbol);
std_bar_foo->set_assigned_name(kFooName);
TestIndexedSymbol std_bar_foo_indexed(module_symbols, std_bar_ns, kFooName, std_bar_foo);
// ::std::$anon::Foo().
auto std_anon_foo = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram);
SymbolTestParentSetter std_anon_foo_parent(std_anon_foo, std_anon_ns_symbol);
std_anon_foo->set_assigned_name(kFooName);
TestIndexedSymbol std_anon_foo_indexed(module_symbols, std_anon_ns, kFooName, std_anon_foo);
// Search for "Foo" in all namespaces.
ParsedIdentifier foo_ident((ParsedIdentifierComponent(kFooName)));
FindNameOptions opts(FindNameOptions::kAllKinds);
opts.max_results = 100; // Want everything.
opts.search_mode = FindNameOptions::kAllNamespaces;
std::vector<FoundName> results;
FindName(context, opts, foo_ident, &results);
// It should have found all 3 Foo's in order.
ASSERT_EQ(4u, results.size());
EXPECT_EQ(foo.get(), results[0].function().get());
EXPECT_EQ(std_foo.get(), results[1].function().get());
EXPECT_EQ(std_anon_foo.get(), results[2].function().get());
EXPECT_EQ(std_bar_foo.get(), results[3].function().get());
// Now find by prefix recursively.
FindNameOptions prefix_opts = opts;
prefix_opts.how = FindNameOptions::kPrefix;
results.clear();
FindName(context, prefix_opts, ParsedIdentifier(ParsedIdentifierComponent("F")), &results);
// Should have found the same matches.
ASSERT_EQ(4u, results.size());
EXPECT_EQ(foo.get(), results[0].function().get());
EXPECT_EQ(std_foo.get(), results[1].function().get());
EXPECT_EQ(std_anon_foo.get(), results[2].function().get());
EXPECT_EQ(std_bar_foo.get(), results[3].function().get());
// Find "bar::Foo" should find only the one match, using the implicit toplevel namespace.
ParsedIdentifier bar_foo;
bar_foo.AppendComponent(ParsedIdentifierComponent("bar"));
bar_foo.AppendComponent(ParsedIdentifierComponent("Foo"));
results.clear();
FindName(context, opts, bar_foo, &results);
ASSERT_EQ(1u, results.size());
EXPECT_EQ(std_bar_foo.get(), results[0].function().get());
// Find "::Foo" should only find the toplevel one, even with implicit namespace searching.
ParsedIdentifier abs_foo(IdentifierQualification::kGlobal, ParsedIdentifierComponent(kFooName));
results.clear();
FindName(context, opts, abs_foo, &results);
ASSERT_EQ(1u, results.size());
EXPECT_EQ(foo.get(), results[0].function().get());
// Find "::std::Foo" should find both ::std::Foo and the anonymous namespace one.
ParsedIdentifier abs_std_foo(IdentifierQualification::kGlobal,
ParsedIdentifierComponent(kStdName));
abs_std_foo.AppendComponent(ParsedIdentifierComponent(kFooName));
results.clear();
FindName(context, opts, abs_std_foo, &results);
ASSERT_EQ(2u, results.size());
EXPECT_EQ(std_foo.get(), results[0].function().get());
EXPECT_EQ(std_anon_foo.get(), results[1].function().get());
}
// Tests that $elf(...) symbols look up properly.
TEST(FindName, ElfSymbol) {
ProcessSymbolsTestSetup setup;
MockModuleSymbols* module_symbols = setup.InjectMockModule();
SymbolContext symbol_context(ProcessSymbolsTestSetup::kDefaultLoadAddress);
FindNameContext context(&setup.process(), symbol_context);
const char kRawName[] = "pthread_key_create";
ParsedIdentifier elf_name(IdentifierQualification::kRelative,
ParsedIdentifierComponent(SpecialIdentifier::kElf, kRawName));
auto elf_symbol = fxl::MakeRefCounted<ElfSymbol>(
module_symbols->GetWeakPtr(), ElfSymbolRecord(ElfSymbolType::kPlt, 0x1234, 4, kRawName));
Location elf_location(0x12345678, FileLine(), 0, symbol_context, elf_symbol);
// The current implementation does not add ELF symbols to the main index although this might be a
// nice thing to do in the future. So for now, we just need to test that manually injected ELF
// symbols come out the other end.
module_symbols->AddSymbolLocations(ToIdentifier(elf_name), {elf_location});
FindNameOptions opts(FindNameOptions::kAllKinds);
std::vector<FoundName> results;
FindName(context, opts, elf_name, &results);
ASSERT_EQ(1u, results.size());
ASSERT_TRUE(results[0].other_symbol());
EXPECT_EQ(results[0].other_symbol().get(), elf_symbol.get());
}
} // namespace zxdb