blob: 7d8683c310d0cf60206b89d572c30cc4ee9f4227 [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/resolve_ptr_ref.h"
#include "gtest/gtest.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_value.h"
#include "src/developer/debug/zxdb/expr/mock_eval_context.h"
#include "src/developer/debug/zxdb/symbols/base_type.h"
#include "src/developer/debug/zxdb/symbols/modified_type.h"
#include "src/developer/debug/zxdb/symbols/type_test_support.h"
namespace zxdb {
namespace {
class ResolvePtrRefTest : public TestWithLoop {};
} // namespace
TEST_F(ResolvePtrRefTest, NotPointer) {
auto eval_context = fxl::MakeRefCounted<MockEvalContext>();
auto int32_type = MakeInt32Type();
ExprValue int32_value(int32_type, {0x00, 0x00, 0x00, 0x00});
bool called = false;
Err out_err;
ExprValue out_value;
ResolvePointer(eval_context, int32_value,
[&called, &out_err, &out_value](const Err& err, ExprValue value) {
called = true;
out_err = err;
out_value = value;
});
// This should fail synchronously.
EXPECT_TRUE(called);
EXPECT_EQ("Attempting to dereference 'int32_t' which is not a pointer.", out_err.msg());
// Pointer with incorrectly sized data.
auto int32_ptr_type =
fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, LazySymbol(int32_type));
ExprValue int32_ptr_value(int32_ptr_type, {0x00, 0x00, 0x00, 0x00});
called = false;
ResolvePointer(eval_context, int32_ptr_value,
[&called, &out_err, &out_value](const Err& err, ExprValue value) {
called = true;
out_err = err;
out_value = value;
});
// This should fail synchronously.
EXPECT_TRUE(called);
EXPECT_EQ(
"The value of type 'int32_t*' is the incorrect size (expecting 8, got "
"4). Please file a bug.",
out_err.msg());
}
TEST_F(ResolvePtrRefTest, InvalidMemory) {
auto eval_context = fxl::MakeRefCounted<MockEvalContext>();
constexpr uint64_t kAddress = 0x10;
auto int32_type = MakeInt32Type();
// This read will return no data.
bool called = false;
Err out_err;
ExprValue out_value;
ResolvePointer(eval_context, kAddress, int32_type,
[&called, &out_err, &out_value](const Err& err, ExprValue value) {
called = true;
out_err = err;
out_value = value;
debug_ipc::MessageLoop::Current()->QuitNow();
});
EXPECT_FALSE(called);
loop().Run();
EXPECT_TRUE(called);
EXPECT_EQ("Invalid pointer 0x10", out_err.msg());
// This read will return only 2 bytes (it requires 4).
eval_context->data_provider()->AddMemory(kAddress, {0x00, 0x00});
called = false;
out_err = Err();
ResolvePointer(eval_context, kAddress, int32_type,
[&called, &out_err, &out_value](const Err& err, ExprValue value) {
called = true;
out_err = err;
out_value = value;
debug_ipc::MessageLoop::Current()->QuitNow();
});
EXPECT_FALSE(called);
loop().Run();
EXPECT_TRUE(called);
EXPECT_EQ("Invalid pointer 0x10", out_err.msg());
}
// Tests EnsureResolveReference when the value is not a reference.
TEST_F(ResolvePtrRefTest, NotRef) {
auto eval_context = fxl::MakeRefCounted<MockEvalContext>();
auto int32_type = MakeInt32Type();
ExprValue value(123);
bool called = false;
ExprValue out_value;
EnsureResolveReference(eval_context, value,
[&called, &out_value](const Err& err, ExprValue result) {
EXPECT_FALSE(err.has_error());
called = true;
out_value = result;
});
// Should have run synchronously.
EXPECT_TRUE(called);
EXPECT_EQ(value, out_value);
}
// Tests EnsureResolveReference when the value is a const ref. The const should
// get ignored, the ref should be stripped, and the pointed-to value should be
// the result.
TEST_F(ResolvePtrRefTest, ConstRef) {
auto eval_context = fxl::MakeRefCounted<MockEvalContext>();
// Add a 32-bit int at a given address.
constexpr uint64_t kAddress = 0x300020;
std::vector<uint8_t> int_value = {0x04, 0x03, 0x02, 0x01};
eval_context->data_provider()->AddMemory(kAddress, int_value);
// Make "volatile const int32_t&". This tests modifies on both sides of the
// reference (volatile on the outside, const on the inside).
auto int32_type = MakeInt32Type();
auto const_int32_type =
fxl::MakeRefCounted<ModifiedType>(DwarfTag::kConstType, LazySymbol(int32_type));
auto const_int32_ref_type =
fxl::MakeRefCounted<ModifiedType>(DwarfTag::kReferenceType, LazySymbol(const_int32_type));
auto volatile_const_int32_ref_type =
fxl::MakeRefCounted<ModifiedType>(DwarfTag::kVolatileType, LazySymbol(const_int32_ref_type));
ExprValue value(volatile_const_int32_ref_type, {0x20, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00});
bool called = false;
ExprValue out_value;
EnsureResolveReference(eval_context, value,
[&called, &out_value](const Err& err, ExprValue result) {
EXPECT_FALSE(err.has_error());
called = true;
out_value = result;
debug_ipc::MessageLoop::Current()->QuitNow();
});
// Should have run asynchronously.
EXPECT_FALSE(called);
loop().Run();
EXPECT_TRUE(called);
// The resolved type should be "const int32_t" and have the input value.
EXPECT_EQ(const_int32_type.get(), out_value.type());
EXPECT_EQ(int_value, out_value.data());
// Check the location of the result.
EXPECT_EQ(ExprValueSource::Type::kMemory, out_value.source().type());
EXPECT_EQ(kAddress, out_value.source().address());
}
TEST_F(ResolvePtrRefTest, GetPointedToType_Null) {
auto eval_context = fxl::MakeRefCounted<MockEvalContext>();
fxl::RefPtr<Type> pointed_to;
Err err = GetPointedToType(eval_context, nullptr, &pointed_to);
EXPECT_TRUE(err.has_error());
EXPECT_EQ("No type information.", err.msg());
}
TEST_F(ResolvePtrRefTest, GetPointedToType_NotPointer) {
auto eval_context = fxl::MakeRefCounted<MockEvalContext>();
auto int32_type = fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeSigned, 4, "int32_t");
fxl::RefPtr<Type> pointed_to;
Err err = GetPointedToType(eval_context, int32_type.get(), &pointed_to);
EXPECT_TRUE(err.has_error());
EXPECT_EQ("Attempting to dereference 'int32_t' which is not a pointer.", err.msg());
}
TEST_F(ResolvePtrRefTest, GetPointedToType_NoPointedToType) {
auto eval_context = fxl::MakeRefCounted<MockEvalContext>();
// Pointer to nothing.
auto ptr_type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, LazySymbol());
fxl::RefPtr<Type> pointed_to;
Err err = GetPointedToType(eval_context, ptr_type.get(), &pointed_to);
EXPECT_TRUE(err.has_error());
EXPECT_EQ("Missing pointer type info, please file a bug with a repro.", err.msg());
}
TEST_F(ResolvePtrRefTest, GetPointedToType_Good) {
auto eval_context = fxl::MakeRefCounted<MockEvalContext>();
auto int32_type = fxl::MakeRefCounted<BaseType>(BaseType::kBaseTypeSigned, 4, "int32_t");
auto ptr_type = fxl::MakeRefCounted<ModifiedType>(DwarfTag::kPointerType, LazySymbol(int32_type));
fxl::RefPtr<Type> pointed_to;
Err err = GetPointedToType(eval_context, ptr_type.get(), &pointed_to);
EXPECT_FALSE(err.has_error()) << err.msg();
EXPECT_EQ(int32_type.get(), pointed_to.get());
}
} // namespace zxdb