blob: 8a03f76102c22fdc837128ac7338085a5181956a [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 "garnet/bin/zxdb/expr/symbol_variable_resolver.h"
#include "garnet/bin/zxdb/expr/expr_value.h"
#include "garnet/bin/zxdb/symbols/base_type.h"
#include "garnet/bin/zxdb/symbols/mock_symbol_data_provider.h"
#include "garnet/bin/zxdb/symbols/symbol_context.h"
#include "garnet/bin/zxdb/symbols/type_test_support.h"
#include "garnet/bin/zxdb/symbols/variable_test_support.h"
#include "garnet/lib/debug_ipc/helper/platform_message_loop.h"
#include "gtest/gtest.h"
#include "llvm/BinaryFormat/Dwarf.h"
namespace zxdb {
namespace {
class SymbolVariableResolverTest : public testing::Test {
public:
SymbolVariableResolverTest()
: provider_(fxl::MakeRefCounted<MockSymbolDataProvider>()) {
loop_.Init();
}
~SymbolVariableResolverTest() { loop_.Cleanup(); }
fxl::RefPtr<MockSymbolDataProvider> provider() { return provider_; }
debug_ipc::MessageLoop& loop() { return loop_; }
private:
debug_ipc::PlatformMessageLoop loop_;
fxl::RefPtr<MockSymbolDataProvider> provider_;
};
} // namespace
// Test a lookup of the value where everything is found.
TEST_F(SymbolVariableResolverTest, FoundAndNot) {
constexpr uint64_t kValue = 0x1234567890123;
provider()->AddRegisterValue(0, true, kValue);
provider()->set_ip(0x1010);
// Declare a variable in register 0.
auto var = MakeUint64VariableForTest(
"present", 0x1000, 0x2000,
{llvm::dwarf::DW_OP_reg0, llvm::dwarf::DW_OP_stack_value});
SymbolContext symbol_context = SymbolContext::ForRelativeAddresses();
SymbolVariableResolver resolver(provider());
bool called = false;
Err out_err;
ExprValue out_value;
resolver.ResolveVariable(
symbol_context, var.get(),
[&called, &out_err, &out_value](const Err& err, ExprValue value) {
called = true;
out_err = err;
out_value = value;
});
EXPECT_TRUE(called);
EXPECT_FALSE(out_err.has_error()) << out_err.msg();
EXPECT_EQ(ExprValue(kValue), out_value);
}
// This lookup has the IP out of range of the variable's validity.
TEST_F(SymbolVariableResolverTest, RangeMiss) {
constexpr uint64_t kValue = 0x1234567890123;
provider()->AddRegisterValue(0, true, kValue);
provider()->set_ip(0x3000);
auto var = MakeUint64VariableForTest(
"present", 0x1000, 0x2000,
{llvm::dwarf::DW_OP_reg0, llvm::dwarf::DW_OP_stack_value});
SymbolContext symbol_context = SymbolContext::ForRelativeAddresses();
SymbolVariableResolver resolver(provider());
bool called = false;
Err out_err;
ExprValue out_value;
resolver.ResolveVariable(
symbol_context, var.get(),
[&called, &out_err, &out_value](const Err& err, ExprValue value) {
called = true;
out_err = err;
out_value = value;
});
EXPECT_TRUE(called);
EXPECT_TRUE(out_err.has_error());
EXPECT_EQ(ErrType::kOptimizedOut, out_err.type());
EXPECT_EQ(ExprValue(), out_value);
}
// Tests the DWARF expression evaluation failing (empty expression).
TEST_F(SymbolVariableResolverTest, DwarfEvalFailure) {
auto var = MakeUint64VariableForTest("present", 0x1000, 0x2000, {});
provider()->set_ip(0x1000);
SymbolContext symbol_context = SymbolContext::ForRelativeAddresses();
SymbolVariableResolver resolver(provider());
bool called = false;
Err out_err;
ExprValue out_value;
resolver.ResolveVariable(
symbol_context, var.get(),
[&called, &out_err, &out_value](const Err& err, ExprValue value) {
called = true;
out_err = err;
out_value = value;
});
EXPECT_TRUE(called);
EXPECT_TRUE(out_err.has_error());
EXPECT_EQ("DWARF expression produced no results.", out_err.msg());
EXPECT_EQ(ExprValue(), out_value);
}
TEST_F(SymbolVariableResolverTest, CharValue) {
constexpr uint64_t kValue = 0x1234567890123;
constexpr int8_t kValueLo = 0x23; // Low byte of kValue.
provider()->AddRegisterValue(0, true, kValue);
provider()->set_ip(0x1010);
// Make a variable and override type with a "char" type.
auto var = MakeUint64VariableForTest(
"present", 0x1000, 0x2000,
{llvm::dwarf::DW_OP_reg0, llvm::dwarf::DW_OP_stack_value});
auto type =
fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeSigned, 1, "char");
var->set_type(LazySymbol(type));
SymbolContext symbol_context = SymbolContext::ForRelativeAddresses();
SymbolVariableResolver resolver(provider());
bool called = false;
Err out_err;
ExprValue out_value;
resolver.ResolveVariable(
symbol_context, var.get(),
[&called, &out_err, &out_value](const Err& err, ExprValue value) {
called = true;
out_err = err;
out_value = value;
});
EXPECT_TRUE(called);
EXPECT_FALSE(out_err.has_error()) << out_err.msg();
// Should have inherited our type.
EXPECT_EQ(type.get(), out_value.type());
ASSERT_EQ(1u, out_value.data().size());
EXPECT_EQ(kValueLo, static_cast<int8_t>(out_value.data()[0]));
EXPECT_EQ(ExprValue(kValueLo), out_value);
}
// 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(SymbolVariableResolverTest, 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(LazySymbol(type));
constexpr uint64_t kBp = 0x1000;
provider()->set_bp(kBp);
provider()->set_ip(0x1000);
provider()->AddMemory(kBp + kOffset, {0x78, 0x56, 0x34, 0x12});
SymbolContext symbol_context = SymbolContext::ForRelativeAddresses();
SymbolVariableResolver resolver(provider());
bool called = false;
Err out_err;
ExprValue out_value;
resolver.ResolveVariable(
symbol_context, var.get(),
[&called, &out_err, &out_value](const Err& err, ExprValue value) {
called = true;
out_err = err;
out_value = value;
debug_ipc::MessageLoop::Current()->QuitNow();
});
// Should be run async since it requests memory.
EXPECT_FALSE(called);
EXPECT_FALSE(out_err.has_error()) << out_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});
bool called2 = false;
Err out_err2;
ExprValue out_value2;
resolver.ResolveVariable(
symbol_context, rangemiss.get(),
[&called2, &out_err2, &out_value2](const Err& err, ExprValue value) {
called2 = true;
out_err2 = err;
out_value2 = value;
});
EXPECT_TRUE(called2);
EXPECT_TRUE(out_err2.has_error());
EXPECT_EQ(ErrType::kOptimizedOut, out_err2.type());
EXPECT_EQ(ExprValue(), out_value2);
// Now let the first request complete.
loop().Run();
EXPECT_TRUE(called);
EXPECT_FALSE(out_err.has_error()) << out_err.msg();
EXPECT_EQ(ExprValue(kValue), out_value);
}
} // namespace zxdb