| // 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/expr/resolve_base.h" |
| |
| #include <gtest/gtest.h> |
| |
| #include "llvm/BinaryFormat/Dwarf.h" |
| #include "src/developer/debug/zxdb/common/test_with_loop.h" |
| #include "src/developer/debug/zxdb/expr/abi_null.h" |
| #include "src/developer/debug/zxdb/expr/mock_eval_context.h" |
| #include "src/developer/debug/zxdb/expr/test_eval_context_impl.h" |
| #include "src/developer/debug/zxdb/expr/virtual_base_test_setup.h" |
| #include "src/developer/debug/zxdb/symbols/collection.h" |
| #include "src/developer/debug/zxdb/symbols/elf_symbol.h" |
| #include "src/developer/debug/zxdb/symbols/inherited_from.h" |
| #include "src/developer/debug/zxdb/symbols/location.h" |
| #include "src/developer/debug/zxdb/symbols/mock_module_symbols.h" |
| #include "src/developer/debug/zxdb/symbols/mock_symbol_data_provider.h" |
| #include "src/developer/debug/zxdb/symbols/modified_type.h" |
| #include "src/developer/debug/zxdb/symbols/process_symbols_test_setup.h" |
| #include "src/developer/debug/zxdb/symbols/type_test_support.h" |
| |
| namespace zxdb { |
| |
| namespace { |
| |
| class ResolveBase : public TestWithLoop {}; |
| |
| } // namespace |
| |
| // Given a class without a vtable, verifies that the DerivedTypeForVtable does a synchronous no-op. |
| TEST_F(ResolveBase, PromotePtrRefToDerived_NoVtable) { |
| auto eval_context = fxl::MakeRefCounted<MockEvalContext>(); |
| |
| auto not_virtual = |
| MakeCollectionType(DwarfTag::kStructureType, "MyStruct", {{"a", MakeInt32Type()}}); |
| std::vector<uint8_t> data{42, 0, 0, 0}; // int32_t = 42. |
| |
| ExprValue value(not_virtual, data); |
| |
| bool called = false; |
| PromotePtrRefToDerived(eval_context, PromoteToDerived::kPtrOrRef, value, |
| [&called, original_value = value](ErrOrValue result) { |
| called = true; |
| EXPECT_TRUE(result.ok()); |
| EXPECT_EQ(original_value, result.value()); |
| |
| // In this test the type object pointers should be the same (not normally |
| // tested in value equality) since the value should be the same one just |
| // forwarded. |
| EXPECT_EQ(original_value.type(), result.value().type()); |
| }); |
| EXPECT_TRUE(called); |
| } |
| |
| TEST_F(ResolveBase, PromotePtrRefToDerived) { |
| ProcessSymbolsTestSetup symbol_setup; |
| MockModuleSymbols* mock_module_symbols = symbol_setup.InjectMockModule(); |
| SymbolContext symbol_context(ProcessSymbolsTestSetup::kDefaultLoadAddress); |
| |
| auto symbol_data_provider = fxl::MakeRefCounted<MockSymbolDataProvider>(); |
| auto eval_context = fxl::MakeRefCounted<TestEvalContextImpl>( |
| std::make_shared<AbiNull>(), symbol_setup.process().GetWeakPtr(), symbol_data_provider, |
| ExprLanguage::kC); |
| |
| VirtualBaseTestSetup setup(symbol_data_provider.get(), mock_module_symbols); |
| |
| // Add a bunch of qualifiers to make sure they come out the other end. |
| auto const_base_class = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kConstType, setup.base_class); |
| auto ptr_const_base_class = |
| fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, const_base_class); |
| auto const_ptr_const_base_class = |
| fxl::MakeRefCounted<ModifiedType>(DwarfTag::kConstType, ptr_const_base_class); |
| |
| // Input Base*. |
| ExprValue base_ptr(setup.kBaseAddress, const_ptr_const_base_class); |
| |
| // ----------------------------------------------------------------------------------------------- |
| // Part 1: vtable pointer points to "Derived". |
| |
| // The default setup will have the vtable point to the derived class. |
| ErrOrValue result(Err("Not called")); |
| PromotePtrRefToDerived(eval_context, PromoteToDerived::kPtrOrRef, base_ptr, |
| [&result](ErrOrValue r) { result = r; }); |
| loop().RunUntilNoTasks(); |
| ASSERT_TRUE(result.ok()); |
| |
| // Now that the memory has been hooked up, the result should be a const*const (consts copied from |
| // the original base type) with the derived address. |
| uint64_t result64 = 0; |
| ASSERT_TRUE(result.value().PromoteTo64(&result64).ok()); |
| EXPECT_EQ(setup.kDerivedAddress, result64); |
| EXPECT_EQ("const DerivedClass* const", result.value().type()->GetFullName()); |
| |
| // ----------------------------------------------------------------------------------------------- |
| // Part 2: vtable pointer points to "Base". |
| |
| // Fix up the vtable pointer to the base class. |
| Location vtable_location(setup.kVtableAbsoluteAddress, FileLine(), 0, symbol_context, |
| setup.base_vtable); |
| mock_module_symbols->AddSymbolLocations(setup.kVtableAbsoluteAddress, {vtable_location}); |
| |
| result = Err("Not called"); |
| PromotePtrRefToDerived(eval_context, PromoteToDerived::kPtrOrRef, base_ptr, |
| [&result](ErrOrValue r) { result = r; }); |
| loop().RunUntilNoTasks(); |
| ASSERT_TRUE(result.ok()) << result.err().msg(); |
| EXPECT_EQ(base_ptr, result.value()); // Should give same input as output. |
| |
| // ----------------------------------------------------------------------------------------------- |
| // Part 3: vtable pointer is invalid. |
| |
| // Declare no symbol at this address. |
| mock_module_symbols->AddSymbolLocations( |
| setup.kVtableAbsoluteAddress, |
| {Location(Location::State::kSymbolized, setup.kVtableAbsoluteAddress)}); |
| |
| // Should run asynchronously and produce success. |
| result = Err("Not called"); |
| PromotePtrRefToDerived(eval_context, PromoteToDerived::kPtrOrRef, base_ptr, |
| [&result](ErrOrValue r) { result = r; }); |
| loop().RunUntilNoTasks(); |
| ASSERT_TRUE(result.ok()); |
| |
| // We did not hook up the vtable memory above so the resolution will fail. It should fall back on |
| // returning the input rather than forwarding an error. |
| EXPECT_EQ(base_ptr, result.value()); |
| EXPECT_EQ("const BaseClass* const", result.value().type()->GetFullName()); |
| |
| // ----------------------------------------------------------------------------------------------- |
| // Part 4: virtual inheritance means we can't promote to derived. |
| |
| // Put back the good derived vtable location cleared in the previous step so it will succeed. |
| mock_module_symbols->AddSymbolLocations(setup.kVtableAbsoluteAddress, |
| {Location(setup.kVtableAbsoluteAddress, FileLine(), 0, |
| symbol_context, setup.derived_vtable)}); |
| |
| // Replace the inheritance record with one indicating virtual inheritance. This placeholder |
| // expression won't work in practice (see VirtualInheritanceTestSetup for a real one) but the |
| // presence of some expression will trigger a casting failure. |
| auto virtual_inheritance = |
| fxl::MakeRefCounted<InheritedFrom>(setup.base_class, DwarfExpr({llvm::dwarf::DW_OP_dup})); |
| setup.derived_class->set_inherited_from({LazySymbol(virtual_inheritance)}); |
| |
| result = Err("Not called"); |
| PromotePtrRefToDerived(eval_context, PromoteToDerived::kPtrOrRef, base_ptr, |
| [&result](ErrOrValue r) { result = r; }); |
| loop().RunUntilNoTasks(); |
| ASSERT_TRUE(result.ok()) << result.err().msg(); |
| EXPECT_EQ(base_ptr, result.value()); // Should give same input as output. |
| } |
| |
| } // namespace zxdb |