|  | // 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/eval_context_impl.h" | 
|  |  | 
|  | #include <gtest/gtest.h> | 
|  |  | 
|  | #include "llvm/BinaryFormat/Dwarf.h" | 
|  | #include "src/developer/debug/shared/platform_message_loop.h" | 
|  | #include "src/developer/debug/zxdb/common/err.h" | 
|  | #include "src/developer/debug/zxdb/common/test_with_loop.h" | 
|  | #include "src/developer/debug/zxdb/expr/expr_node.h" | 
|  | #include "src/developer/debug/zxdb/expr/expr_parser.h" | 
|  | #include "src/developer/debug/zxdb/expr/expr_value.h" | 
|  | #include "src/developer/debug/zxdb/symbols/base_type.h" | 
|  | #include "src/developer/debug/zxdb/symbols/code_block.h" | 
|  | #include "src/developer/debug/zxdb/symbols/collection.h" | 
|  | #include "src/developer/debug/zxdb/symbols/compile_unit.h" | 
|  | #include "src/developer/debug/zxdb/symbols/data_member.h" | 
|  | #include "src/developer/debug/zxdb/symbols/dwarf_expr_eval.h" | 
|  | #include "src/developer/debug/zxdb/symbols/function.h" | 
|  | #include "src/developer/debug/zxdb/symbols/index_test_support.h" | 
|  | #include "src/developer/debug/zxdb/symbols/inherited_from.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/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" | 
|  |  | 
|  | namespace zxdb { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // The range of IP addresses where the "present" variable is valid. | 
|  | constexpr uint64_t kBeginValidRange = 0x1000; | 
|  | constexpr uint64_t kEndValidRange = 0x2000; | 
|  |  | 
|  | const char kPresentVarName[] = "present"; | 
|  |  | 
|  | class EvalContextImplTest : public TestWithLoop { | 
|  | public: | 
|  | EvalContextImplTest() : provider_(fxl::MakeRefCounted<MockSymbolDataProvider>()) {} | 
|  |  | 
|  | DwarfExprEval& eval() { return eval_; } | 
|  | fxl::RefPtr<MockSymbolDataProvider>& provider() { return provider_; } | 
|  |  | 
|  | fxl::RefPtr<CodeBlock> MakeCodeBlock() { | 
|  | auto block = fxl::MakeRefCounted<CodeBlock>(DwarfTag::kLexicalBlock); | 
|  |  | 
|  | // Declare a variable in this code block stored in register 0. | 
|  | auto variable = | 
|  | MakeUint64VariableForTest(kPresentVarName, kBeginValidRange, kEndValidRange, | 
|  | {llvm::dwarf::DW_OP_reg0, llvm::dwarf::DW_OP_stack_value}); | 
|  | block->set_variables({LazySymbol(std::move(variable))}); | 
|  |  | 
|  | // TODO(brettw) this needs a type. Currently this test is very simple and only outputs internal | 
|  | // ints. | 
|  | return block; | 
|  | } | 
|  |  | 
|  | // Returns an evaluation context for a code block. If the code block is null, a default one will | 
|  | // be created with MakeCodeBlock(). | 
|  | fxl::RefPtr<EvalContext> MakeEvalContext(fxl::RefPtr<CodeBlock> code_block = nullptr) { | 
|  | return fxl::MakeRefCounted<EvalContextImpl>(fxl::WeakPtr<const ProcessSymbols>(), provider(), | 
|  | ExprLanguage::kC, | 
|  | code_block ? code_block : MakeCodeBlock()); | 
|  | } | 
|  |  | 
|  | private: | 
|  | DwarfExprEval eval_; | 
|  | fxl::RefPtr<MockSymbolDataProvider> provider_; | 
|  | }; | 
|  |  | 
|  | struct ValueResult { | 
|  | ValueResult() : value(ExprValue()) {} | 
|  |  | 
|  | bool called = false;  // Set when the callback is issued. | 
|  | ErrOrValue value; | 
|  | }; | 
|  |  | 
|  | // Wrapper around eval_context->GetNamedValue that places the callback parameters into a struct. It | 
|  | // makes the callsites cleaner. | 
|  | void GetNamedValue(const fxl::RefPtr<EvalContext>& eval_context, const std::string& name, | 
|  | ValueResult* result) { | 
|  | ParsedIdentifier ident; | 
|  | Err err = ExprParser::ParseIdentifier(name, &ident); | 
|  | ASSERT_FALSE(err.has_error()) << name; | 
|  |  | 
|  | eval_context->GetNamedValue(ident, [result](ErrOrValue value) { | 
|  | result->called = true; | 
|  | result->value = std::move(value); | 
|  | }); | 
|  | } | 
|  |  | 
|  | // Sync wrapper around GetVariableValue(). | 
|  | void GetVariableValue(const fxl::RefPtr<EvalContext>& eval_context, fxl::RefPtr<Variable> variable, | 
|  | ValueResult* result) { | 
|  | eval_context->GetVariableValue(variable, [result](ErrOrValue value) { | 
|  | result->called = true; | 
|  | result->value = std::move(value); | 
|  | }); | 
|  | } | 
|  |  | 
|  | const debug_ipc::RegisterID kDWARFReg0ID = debug_ipc::RegisterID::kARMv8_x0; | 
|  | const debug_ipc::RegisterID kDWARFReg1ID = debug_ipc::RegisterID::kARMv8_x1; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST_F(EvalContextImplTest, NotFoundSynchronous) { | 
|  | provider()->set_ip(0x1010); | 
|  |  | 
|  | auto context = MakeEvalContext(); | 
|  |  | 
|  | ValueResult result; | 
|  | GetNamedValue(context, "not_present", &result); | 
|  |  | 
|  | EXPECT_TRUE(result.called); | 
|  | EXPECT_TRUE(result.value.has_error()); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalContextImplTest, FoundSynchronous) { | 
|  | constexpr uint64_t kValue = 12345678; | 
|  | provider()->set_ip(0x1010); | 
|  | provider()->AddRegisterValue(kDWARFReg0ID, true, kValue); | 
|  |  | 
|  | auto context = MakeEvalContext(); | 
|  |  | 
|  | ValueResult result; | 
|  | GetNamedValue(context, kPresentVarName, &result); | 
|  |  | 
|  | EXPECT_TRUE(result.called); | 
|  | EXPECT_FALSE(result.value.has_error()) << result.value.err().msg(); | 
|  | EXPECT_EQ(ExprValue(kValue), result.value.value()); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalContextImplTest, FoundAsynchronous) { | 
|  | constexpr uint64_t kValue = 12345678; | 
|  | provider()->AddRegisterValue(kDWARFReg0ID, false, kValue); | 
|  | provider()->set_ip(0x1010); | 
|  |  | 
|  | auto context = MakeEvalContext(); | 
|  |  | 
|  | ValueResult result; | 
|  | GetNamedValue(context, kPresentVarName, &result); | 
|  |  | 
|  | // Should not have been called yet since retrieving the register is asynchronous. | 
|  | EXPECT_FALSE(result.called); | 
|  |  | 
|  | // Running the message loop should complete the callback. | 
|  | loop().RunUntilNoTasks(); | 
|  | EXPECT_TRUE(result.called); | 
|  | EXPECT_FALSE(result.value.has_error()) << result.value.err().msg(); | 
|  | EXPECT_EQ(ExprValue(kValue), result.value.value()); | 
|  | } | 
|  |  | 
|  | // Tests finding variables on |this| and subclasses of |this|. | 
|  | TEST_F(EvalContextImplTest, FoundThis) { | 
|  | auto int32_type = MakeInt32Type(); | 
|  | auto derived = | 
|  | MakeDerivedClassPair(DwarfTag::kClassType, "Base", {{"b1", int32_type}, {"b2", int32_type}}, | 
|  | "Derived", {{"d1", int32_type}, {"d2", int32_type}}); | 
|  |  | 
|  | auto derived_ptr = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, derived); | 
|  |  | 
|  | // Make the storage for the class in memory. | 
|  | constexpr uint64_t kObjectAddr = 0x3000; | 
|  | constexpr uint8_t kB1 = 1; | 
|  | constexpr uint8_t kB2 = 2; | 
|  | constexpr uint8_t kD1 = 3; | 
|  | constexpr uint8_t kD2 = 4; | 
|  | provider()->AddMemory(kObjectAddr, {kB1, 0, 0, 0,    // (int32) Base.b1 | 
|  | kB2, 0, 0, 0,    // (int32) Base.b2 | 
|  | kD1, 0, 0, 0,    // (int32) Derived.d1 | 
|  | kD2, 0, 0, 0});  // (int32) Derived.d2 | 
|  |  | 
|  | // Our parameter "Derived* this = kObjectAddr" is passed in register 0. | 
|  | provider()->set_ip(kBeginValidRange); | 
|  | provider()->AddRegisterValue(kDWARFReg0ID, false, kObjectAddr); | 
|  | auto this_var = MakeVariableForTest("this", derived_ptr, kBeginValidRange, kEndValidRange, | 
|  | {llvm::dwarf::DW_OP_reg0, llvm::dwarf::DW_OP_stack_value}); | 
|  |  | 
|  | // Make a function with a parameter / object pointer to Derived (this will be like a member | 
|  | // function on Derived). | 
|  | auto function = fxl::MakeRefCounted<Function>(DwarfTag::kSubprogram); | 
|  | function->set_parameters({LazySymbol(this_var)}); | 
|  | function->set_object_pointer(this_var); | 
|  |  | 
|  | auto context = MakeEvalContext(function); | 
|  |  | 
|  | // First get d2 on the derived class. "this" should be implicit. | 
|  | ValueResult result_d2; | 
|  | GetNamedValue(context, "d2", &result_d2); | 
|  |  | 
|  | // Should not have been called yet since retrieving the register is asynchronous. | 
|  | EXPECT_FALSE(result_d2.called); | 
|  |  | 
|  | // Running the message loop should complete the callback. | 
|  | loop().RunUntilNoTasks(); | 
|  | EXPECT_TRUE(result_d2.called); | 
|  | EXPECT_FALSE(result_d2.value.has_error()) << result_d2.value.err().msg(); | 
|  | EXPECT_EQ(ExprValue(static_cast<uint32_t>(kD2)), result_d2.value.value()); | 
|  |  | 
|  | // Now get b2 on the base class, it should implicitly find it on "this" and then check the base | 
|  | // class. | 
|  | ValueResult result_b2; | 
|  | GetNamedValue(context, "b2", &result_b2); | 
|  |  | 
|  | EXPECT_FALSE(result_b2.called); | 
|  | loop().RunUntilNoTasks(); | 
|  | EXPECT_TRUE(result_b2.called); | 
|  | EXPECT_FALSE(result_b2.value.has_error()) << result_b2.value.err().msg(); | 
|  | EXPECT_EQ(ExprValue(static_cast<uint32_t>(kB2)), result_b2.value.value()); | 
|  | } | 
|  |  | 
|  | // Tests a variable lookup that has the IP out of range of the variable's validity. | 
|  | TEST_F(EvalContextImplTest, RangeMiss) { | 
|  | // Set up a valid register for the variable. A missing register shouldn't be why it fails to be | 
|  | // found. | 
|  | constexpr uint64_t kValue = 0x1234567890123; | 
|  | provider()->AddRegisterValue(kDWARFReg0ID, true, kValue); | 
|  | provider()->set_ip(kEndValidRange + 0x10); | 
|  |  | 
|  | ValueResult result; | 
|  | GetNamedValue(MakeEvalContext(), kPresentVarName, &result); | 
|  | EXPECT_TRUE(result.called); | 
|  | EXPECT_TRUE(result.value.has_error()); | 
|  | EXPECT_EQ(ErrType::kOptimizedOut, result.value.err().type()); | 
|  | } | 
|  |  | 
|  | // Tests the DWARF expression evaluation failing (empty expression). | 
|  | TEST_F(EvalContextImplTest, DwarfEvalFailure) { | 
|  | const char kEmptyExprVarName[] = "empty_expr"; | 
|  | provider()->set_ip(kBeginValidRange); | 
|  |  | 
|  | auto var = MakeUint64VariableForTest(kEmptyExprVarName, kBeginValidRange, kEndValidRange, {}); | 
|  |  | 
|  | auto block = MakeCodeBlock(); | 
|  | block->set_variables({LazySymbol(std::move(var))}); | 
|  |  | 
|  | ValueResult result; | 
|  | GetNamedValue(MakeEvalContext(block), kEmptyExprVarName, &result); | 
|  | EXPECT_TRUE(result.called); | 
|  | ASSERT_TRUE(result.value.has_error()); | 
|  | EXPECT_EQ("DWARF expression produced no results.", result.value.err().msg()); | 
|  | } | 
|  |  | 
|  | // Tests asynchronously reading an integer from memory. This also tests interleaved execution of | 
|  | // multiple requests by having a resolution miss request execute while the memory request is | 
|  | // pending. | 
|  | TEST_F(EvalContextImplTest, IntOnStack) { | 
|  | // Define a 4-byte integer (=0x12345678) at location bp+8 | 
|  | constexpr int32_t kValue = 0x12345678; | 
|  |  | 
|  | constexpr uint8_t kOffset = 8; | 
|  | auto type = MakeInt32Type(); | 
|  | auto var = MakeUint64VariableForTest("i", 0, 0, {llvm::dwarf::DW_OP_fbreg, kOffset}); | 
|  | var->set_type(type); | 
|  |  | 
|  | constexpr uint64_t kBp = 0x1000; | 
|  | provider()->set_bp(kBp); | 
|  | provider()->set_ip(0x1000); | 
|  | provider()->AddMemory(kBp + kOffset, {0x78, 0x56, 0x34, 0x12}); | 
|  |  | 
|  | auto context = MakeEvalContext(); | 
|  |  | 
|  | ValueResult result1; | 
|  | GetVariableValue(context, var, &result1); | 
|  |  | 
|  | // Should be run async since it requests memory. | 
|  | EXPECT_FALSE(result1.called); | 
|  | EXPECT_FALSE(result1.value.has_error()) << result1.value.err().msg(); | 
|  |  | 
|  | // Before running the loop and receiving the memory, start a new request, this one will fail | 
|  | // synchronously due to a range miss. | 
|  | auto rangemiss = | 
|  | MakeUint64VariableForTest("rangemiss", 0x6000, 0x7000, {llvm::dwarf::DW_OP_reg0}); | 
|  | ValueResult result2; | 
|  | GetVariableValue(context, rangemiss, &result2); | 
|  | EXPECT_TRUE(result2.called); | 
|  | EXPECT_TRUE(result2.value.err().has_error()); | 
|  | EXPECT_EQ(ErrType::kOptimizedOut, result2.value.err().type()); | 
|  |  | 
|  | // Now let the first request complete. | 
|  | loop().RunUntilNoTasks(); | 
|  | EXPECT_TRUE(result1.called); | 
|  | ASSERT_FALSE(result1.value.has_error()) << result1.value.err().msg(); | 
|  | EXPECT_EQ(ExprValue(kValue), result1.value.value()); | 
|  |  | 
|  | // Validate variable source annotation. | 
|  | const ExprValueSource& source = result1.value.value().source(); | 
|  | EXPECT_EQ(ExprValueSource::Type::kMemory, source.type()); | 
|  | EXPECT_EQ(kBp + kOffset, source.address()); | 
|  | } | 
|  |  | 
|  | // Checks that constant DWARF expressions result in constant variables. | 
|  | TEST_F(EvalContextImplTest, ConstantVariable) { | 
|  | auto type = MakeInt32Type(); | 
|  | auto var = MakeUint64VariableForTest("i", 0, 0, | 
|  | {llvm::dwarf::DW_OP_lit3, llvm::dwarf::DW_OP_stack_value}); | 
|  | var->set_type(type); | 
|  |  | 
|  | ValueResult result; | 
|  | GetVariableValue(MakeEvalContext(), var, &result); | 
|  | loop().RunUntilNoTasks(); | 
|  |  | 
|  | ASSERT_TRUE(result.called); | 
|  | EXPECT_EQ(3, result.value.value().GetAs<int32_t>()); | 
|  | EXPECT_EQ(ExprValueSource::Type::kConstant, result.value.value().source().type()); | 
|  | } | 
|  |  | 
|  | // Tests that externs are resolved by GetVariableValue(). This requires using the index. | 
|  | TEST_F(EvalContextImplTest, ExternVariable) { | 
|  | // Need to have a module for the variable to be relative to and to have an index. | 
|  | ProcessSymbolsTestSetup setup; | 
|  | MockModuleSymbols* module_symbols = setup.InjectMockModule(); | 
|  | SymbolContext symbol_context(ProcessSymbolsTestSetup::kDefaultLoadAddress); | 
|  |  | 
|  | // Offset from beginning of the module of the data. | 
|  | constexpr uint8_t kRelativeValAddress = 0x99; | 
|  | constexpr uint64_t kAbsoluteValAddress = | 
|  | ProcessSymbolsTestSetup::kDefaultLoadAddress + kRelativeValAddress; | 
|  | const char kValName[] = "val"; | 
|  |  | 
|  | // The non-extern declaration for the variable (0, 0 means always valid). The little-endian | 
|  | // module-relative address follows DW_OP_addr in the expression. | 
|  | auto real_variable = MakeUint64VariableForTest( | 
|  | kValName, 0, 0, {llvm::dwarf::DW_OP_addr, kRelativeValAddress, 0, 0, 0, 0, 0, 0, 0}); | 
|  |  | 
|  | // The variable needs to have a unit that references the module to provide the symbol context | 
|  | // in which to evaluate the location expression. This will convert the kRelativeValAddress to | 
|  | // kAbsoluteValAddress. | 
|  | auto unit = | 
|  | fxl::MakeRefCounted<CompileUnit>(module_symbols->GetWeakPtr(), DwarfLang::kC, "file.cc"); | 
|  | SymbolTestParentSetter var_parent(real_variable, unit); | 
|  |  | 
|  | // A reference to the same variable, marked "external" with no location. | 
|  | auto extern_variable = fxl::MakeRefCounted<Variable>(DwarfTag::kVariable); | 
|  | extern_variable->set_assigned_name(kValName); | 
|  | extern_variable->set_is_external(true); | 
|  | extern_variable->set_type(MakeUint64Type()); | 
|  |  | 
|  | // Index the non-extern variable. | 
|  | TestIndexedSymbol indexed_def(module_symbols, &module_symbols->index().root(), kValName, | 
|  | real_variable); | 
|  |  | 
|  | // Set the value for the non-extern variable in the mocked memory. | 
|  | constexpr uint64_t kValValue = 0x0102030405060708; | 
|  | provider()->AddMemory(kAbsoluteValAddress, {8, 7, 6, 5, 4, 3, 2, 1}); | 
|  |  | 
|  | auto context = fxl::MakeRefCounted<EvalContextImpl>(setup.process().GetWeakPtr(), provider(), | 
|  | ExprLanguage::kC, MakeCodeBlock()); | 
|  |  | 
|  | // Resolving the extern variable should give the value that the non-extern one points to. | 
|  | ValueResult result; | 
|  | GetVariableValue(context, extern_variable, &result); | 
|  | loop().RunUntilNoTasks(); | 
|  | ASSERT_TRUE(result.called); | 
|  | ASSERT_TRUE(result.value.ok()) << result.value.err().msg(); | 
|  | EXPECT_EQ(ExprValue(kValValue), result.value.value()); | 
|  | } | 
|  |  | 
|  | // This is a larger test that runs the EvalContext through ExprNode.Eval. | 
|  | TEST_F(EvalContextImplTest, NodeIntegation) { | 
|  | constexpr uint64_t kValue = 12345678; | 
|  | provider()->AddRegisterValue(kDWARFReg0ID, false, kValue); | 
|  | provider()->set_ip(kBeginValidRange + 0x10); | 
|  |  | 
|  | auto context = MakeEvalContext(); | 
|  |  | 
|  | // Look up an identifier that's not present. | 
|  | auto present = fxl::MakeRefCounted<IdentifierExprNode>(kPresentVarName); | 
|  | bool called = false; | 
|  | ExprValue out_value; | 
|  | present->Eval(context, [&called, &out_value](ErrOrValue value) { | 
|  | called = true; | 
|  | EXPECT_FALSE(value.has_error()); | 
|  | out_value = value.take_value(); | 
|  | }); | 
|  | // Should not have been called yet since retrieving the register is asynchronous. | 
|  | EXPECT_FALSE(called); | 
|  |  | 
|  | loop().RunUntilNoTasks(); | 
|  | EXPECT_TRUE(called); | 
|  | EXPECT_EQ(ExprValue(kValue), out_value); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalContextImplTest, RegisterByName) { | 
|  | ASSERT_EQ(debug_ipc::Arch::kArm64, provider()->GetArch()); | 
|  |  | 
|  | constexpr uint64_t kRegValue = 0xdeadb33f; | 
|  | provider()->AddRegisterValue(kDWARFReg0ID, false, kRegValue); | 
|  | auto context = MakeEvalContext(); | 
|  |  | 
|  | // We've defined no variables*, so this should fall back and give us the register by name. | 
|  | // *(Except kPresentVarName which MakeCodeBlock defines). | 
|  | ValueResult reg; | 
|  | GetNamedValue(context, "x0", ®); | 
|  |  | 
|  | // Should not have been called yet since retrieving the register is asynchronous. | 
|  | EXPECT_FALSE(reg.called); | 
|  |  | 
|  | // Running the message loop should complete the callback. | 
|  | loop().RunUntilNoTasks(); | 
|  | EXPECT_TRUE(reg.called); | 
|  | EXPECT_FALSE(reg.value.has_error()) << reg.value.err().msg(); | 
|  | EXPECT_EQ(ExprValue(static_cast<uint64_t>(kRegValue)), reg.value.value()); | 
|  |  | 
|  | // Test again, this time with $ prefix | 
|  | reg.called = false; | 
|  | GetNamedValue(context, "$reg(x0)", ®); | 
|  |  | 
|  | EXPECT_FALSE(reg.called); | 
|  |  | 
|  | loop().RunUntilNoTasks(); | 
|  | EXPECT_TRUE(reg.called); | 
|  | EXPECT_FALSE(reg.value.has_error()) << reg.value.err().msg(); | 
|  | EXPECT_EQ(ExprValue(static_cast<uint64_t>(kRegValue)), reg.value.value()); | 
|  |  | 
|  | // The value source should map back to the input register. | 
|  | const ExprValueSource& source = reg.value.value().source(); | 
|  | EXPECT_EQ(ExprValueSource::Type::kRegister, source.type()); | 
|  | EXPECT_EQ(debug_ipc::RegisterID::kARMv8_x0, source.register_id()); | 
|  | EXPECT_FALSE(source.is_bitfield()); | 
|  |  | 
|  | // This register is synchronously known unavailable. | 
|  | provider()->AddRegisterValue(debug_ipc::RegisterID::kARMv8_x2, true, std::vector<uint8_t>{}); | 
|  | reg.called = false; | 
|  | GetNamedValue(context, "x2", ®); | 
|  | ASSERT_TRUE(reg.called); | 
|  | ASSERT_TRUE(reg.value.has_error()); | 
|  | EXPECT_EQ("Register x2 unavailable in this context.", reg.value.err().msg()); | 
|  |  | 
|  | // This register is synchronously unavailable. | 
|  | provider()->AddRegisterValue(debug_ipc::RegisterID::kARMv8_x3, false, std::vector<uint8_t>{}); | 
|  | reg.called = false; | 
|  | GetNamedValue(context, "x3", ®); | 
|  | ASSERT_FALSE(reg.called); | 
|  | loop().RunUntilNoTasks(); | 
|  | EXPECT_TRUE(reg.called); | 
|  | ASSERT_TRUE(reg.value.has_error()); | 
|  | EXPECT_EQ("Register x3 unavailable in this context.", reg.value.err().msg()); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalContextImplTest, RegisterShadowed) { | 
|  | constexpr uint64_t kRegValue = 0xdeadb33f; | 
|  | constexpr uint64_t kVarValue = 0xf00db4be; | 
|  |  | 
|  | auto shadow_var = | 
|  | MakeUint64VariableForTest("x0", kBeginValidRange, kEndValidRange, | 
|  | {llvm::dwarf::DW_OP_reg1, llvm::dwarf::DW_OP_stack_value}); | 
|  |  | 
|  | auto block = MakeCodeBlock(); | 
|  | block->set_variables({LazySymbol(std::move(shadow_var))}); | 
|  |  | 
|  | provider()->set_ip(kBeginValidRange); | 
|  | provider()->AddRegisterValue(kDWARFReg0ID, false, kRegValue); | 
|  | provider()->AddRegisterValue(kDWARFReg1ID, false, kVarValue); | 
|  | auto context = MakeEvalContext(block); | 
|  |  | 
|  | // This should just look up our variable, x0, which is in the register x1. If It looks up the | 
|  | // register x0 something has gone very wrong. | 
|  | ValueResult val; | 
|  | GetNamedValue(context, "x0", &val); | 
|  |  | 
|  | // Should not have been called yet since retrieving the register is asynchronous. | 
|  | EXPECT_FALSE(val.called); | 
|  |  | 
|  | // Running the message loop should complete the callback. | 
|  | loop().RunUntilNoTasks(); | 
|  | EXPECT_TRUE(val.called); | 
|  | ASSERT_FALSE(val.value.has_error()) << val.value.err().msg(); | 
|  | EXPECT_EQ(ExprValue(static_cast<uint64_t>(kVarValue)), val.value.value()); | 
|  |  | 
|  | // $ prefix should make the register show through. | 
|  | val.called = false; | 
|  | GetNamedValue(context, "$reg(x0)", &val); | 
|  |  | 
|  | EXPECT_FALSE(val.called); | 
|  |  | 
|  | loop().RunUntilNoTasks(); | 
|  | EXPECT_TRUE(val.called); | 
|  | EXPECT_FALSE(val.value.has_error()) << val.value.err().msg(); | 
|  | EXPECT_EQ(ExprValue(static_cast<uint64_t>(kRegValue)), val.value.value()); | 
|  | } | 
|  |  | 
|  | // Tests that a < 64-bit register is read into a value of the correct size, and that the | 
|  | // pseudoregisters referring to a sub-part of a canonical register are working properly. | 
|  | TEST_F(EvalContextImplTest, RegisterShort) { | 
|  | ASSERT_EQ(debug_ipc::Arch::kArm64, provider()->GetArch()); | 
|  |  | 
|  | constexpr uint64_t kRegValue = 0x44332211; | 
|  | provider()->AddRegisterValue(debug_ipc::RegisterID::kARMv8_w0, true, {0x11, 0x22, 0x33, 0x44}); | 
|  | auto context = MakeEvalContext(); | 
|  |  | 
|  | // "w0" is the ARM64 way to refer to the low 32-bits of the "x0" register we set above. | 
|  | ValueResult reg; | 
|  | GetNamedValue(context, "w0", ®); | 
|  |  | 
|  | // Above we set the register to be returned synchronously. | 
|  | ASSERT_TRUE(reg.called); | 
|  | ASSERT_FALSE(reg.value.has_error()) << reg.value.err().msg(); | 
|  | EXPECT_EQ(ExprValue(static_cast<uint32_t>(kRegValue)), reg.value.value()); | 
|  | EXPECT_EQ("uint32_t", reg.value.value().type()->GetFullName()); | 
|  |  | 
|  | // Check source mapping. | 
|  | const ExprValueSource& source = reg.value.value().source(); | 
|  | EXPECT_EQ(ExprValueSource::Type::kRegister, source.type()); | 
|  | EXPECT_EQ(debug_ipc::RegisterID::kARMv8_w0, source.register_id()); | 
|  | EXPECT_FALSE(source.is_bitfield()); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalContextImplTest, VectorRegister) { | 
|  | // This just tests that vector formatting for vector registers is hooked up in the EvalContext | 
|  | // rather than trying to test all of the various formats. The EvalContextImpl formats all | 
|  | // vector registers as doubles (in real life the client overrides this to integrate with the | 
|  | // settings system). | 
|  | ASSERT_EQ(debug_ipc::Arch::kArm64, provider()->GetArch()); | 
|  |  | 
|  | // 128-bit vector register. | 
|  | std::vector<uint8_t> data{0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, | 
|  | 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; | 
|  | provider()->AddRegisterValue(debug_ipc::RegisterID::kARMv8_v0, true, data); | 
|  | auto context = MakeEvalContext(); | 
|  |  | 
|  | ValueResult reg; | 
|  | GetNamedValue(context, "v0", ®); | 
|  |  | 
|  | ASSERT_TRUE(reg.called); | 
|  | ASSERT_FALSE(reg.value.has_error()) << reg.value.err().msg(); | 
|  |  | 
|  | EXPECT_EQ("double[2]", reg.value.value().type()->GetFullName()); | 
|  |  | 
|  | // The data should be passed through unchanged, the array code will handle unpacking it. | 
|  | EXPECT_EQ(data, reg.value.value().data()); | 
|  |  | 
|  | // Check source mapping. | 
|  | const ExprValueSource& source = reg.value.value().source(); | 
|  | EXPECT_EQ(ExprValueSource::Type::kRegister, source.type()); | 
|  | EXPECT_EQ(debug_ipc::RegisterID::kARMv8_v0, source.register_id()); | 
|  | EXPECT_FALSE(source.is_bitfield()); | 
|  | } | 
|  |  | 
|  | TEST_F(EvalContextImplTest, DataResult) { | 
|  | // Tests that composite variable locations are properly converted to values. | 
|  | const char kVarName[] = "var"; | 
|  | auto variable = | 
|  | MakeUint64VariableForTest(kVarName, 0, 0, | 
|  | {llvm::dwarf::DW_OP_reg0,           // Low bytes in reg0. | 
|  | llvm::dwarf::DW_OP_piece, 0x04,    // Pick low 4 bytes of reg0. | 
|  | llvm::dwarf::DW_OP_reg1,           // High bytes in reg1. | 
|  | llvm::dwarf::DW_OP_piece, 0x04});  // Pick low 4 of reg1. | 
|  | provider()->AddRegisterValue(debug_ipc::RegisterID::kARMv8_x0, true, 1); | 
|  | provider()->AddRegisterValue(debug_ipc::RegisterID::kARMv8_x1, true, 2); | 
|  |  | 
|  | auto block = MakeCodeBlock(); | 
|  | block->set_variables({LazySymbol(variable)}); | 
|  | auto context = MakeEvalContext(block); | 
|  |  | 
|  | ValueResult val; | 
|  | GetNamedValue(context, kVarName, &val); | 
|  | EXPECT_TRUE(val.called);  // Result should be synchronous. | 
|  |  | 
|  | ASSERT_FALSE(val.value.has_error()) << val.value.err().msg(); | 
|  | std::vector<uint8_t> expected{1, 0, 0, 0, 2, 0, 0, 0}; | 
|  | EXPECT_EQ(expected, val.value.value().data()); | 
|  | EXPECT_EQ(ExprValueSource::Type::kComposite, val.value.value().source().type()); | 
|  | } | 
|  |  | 
|  | }  // namespace zxdb |