[lldb] Make ValueObjectDynamicValue::UpdateValue() point to a host b… (#125143)
…uffer ValueObjectDynamicValue::UpdateValue() assumes that the dynamic type found by GetDynamicTypeAndAddress() would return an address in the inferior. This commit makes it so it can deal with being passed a host address instead. This is needed downstream by the Swift fork. rdar://143357274
This commit is contained in:
parent
6f508492d1
commit
0cbc4983ad
@ -105,12 +105,16 @@ public:
|
||||
"language doesn't support getting vtable information");
|
||||
}
|
||||
|
||||
// this call should return true if it could set the name and/or the type
|
||||
virtual bool GetDynamicTypeAndAddress(ValueObject &in_value,
|
||||
lldb::DynamicValueType use_dynamic,
|
||||
TypeAndOrName &class_type_or_name,
|
||||
Address &address,
|
||||
Value::ValueType &value_type) = 0;
|
||||
/// This call should return true if it could set the name and/or the type
|
||||
/// Sets address to the address of the dynamic type if value_type is set to
|
||||
/// a file or load address. Sets local_buffer to a buffer containing the data
|
||||
/// of the dynamic type if value_type is set to a host address. Callers should
|
||||
/// copy local_buffer over into their own buffer if they want to keep the data
|
||||
/// alive.
|
||||
virtual bool GetDynamicTypeAndAddress(
|
||||
ValueObject &in_value, lldb::DynamicValueType use_dynamic,
|
||||
TypeAndOrName &class_type_or_name, Address &address,
|
||||
Value::ValueType &value_type, llvm::ArrayRef<uint8_t> &local_buffer) = 0;
|
||||
|
||||
// This call should return a CompilerType given a generic type name and an
|
||||
// ExecutionContextScope in which one can actually fetch any specialization
|
||||
|
||||
@ -865,6 +865,18 @@ public:
|
||||
|
||||
virtual void SetLanguageFlags(uint64_t flags) { m_language_flags = flags; }
|
||||
|
||||
/// Returns the size of the local buffer if it's available.
|
||||
/// \return
|
||||
/// The size of the local buffer if this value object's value points to a
|
||||
/// host address, and if that size can be determined. Otherwise, returns
|
||||
/// LLDB_INVALID_ADDRESS.
|
||||
///
|
||||
/// TODO: Because a ValueObject's Value can point to any arbitrary memory
|
||||
/// location, it is possible that the size of the local buffer can't be
|
||||
/// determined at all. See the comment in Value::m_value for a more thorough
|
||||
/// explanation of why that is.
|
||||
uint64_t GetLocalBufferSize();
|
||||
|
||||
protected:
|
||||
typedef ClusterManager<ValueObject> ValueObjectManager;
|
||||
|
||||
|
||||
@ -289,7 +289,7 @@ llvm::Expected<LanguageRuntime::VTableInfo>
|
||||
bool ItaniumABILanguageRuntime::GetDynamicTypeAndAddress(
|
||||
ValueObject &in_value, lldb::DynamicValueType use_dynamic,
|
||||
TypeAndOrName &class_type_or_name, Address &dynamic_address,
|
||||
Value::ValueType &value_type) {
|
||||
Value::ValueType &value_type, llvm::ArrayRef<uint8_t> &local_buffer) {
|
||||
// For Itanium, if the type has a vtable pointer in the object, it will be at
|
||||
// offset 0 in the object. That will point to the "address point" within the
|
||||
// vtable (not the beginning of the vtable.) We can then look up the symbol
|
||||
|
||||
@ -54,8 +54,8 @@ public:
|
||||
bool GetDynamicTypeAndAddress(ValueObject &in_value,
|
||||
lldb::DynamicValueType use_dynamic,
|
||||
TypeAndOrName &class_type_or_name,
|
||||
Address &address,
|
||||
Value::ValueType &value_type) override;
|
||||
Address &address, Value::ValueType &value_type,
|
||||
llvm::ArrayRef<uint8_t> &local_buffer) override;
|
||||
|
||||
TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
|
||||
ValueObject &static_value) override;
|
||||
|
||||
@ -276,7 +276,7 @@ bool AppleObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
|
||||
bool AppleObjCRuntime::GetDynamicTypeAndAddress(
|
||||
ValueObject &in_value, lldb::DynamicValueType use_dynamic,
|
||||
TypeAndOrName &class_type_or_name, Address &address,
|
||||
Value::ValueType &value_type) {
|
||||
Value::ValueType &value_type, llvm::ArrayRef<uint8_t> &local_buffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -54,8 +54,8 @@ public:
|
||||
bool GetDynamicTypeAndAddress(ValueObject &in_value,
|
||||
lldb::DynamicValueType use_dynamic,
|
||||
TypeAndOrName &class_type_or_name,
|
||||
Address &address,
|
||||
Value::ValueType &value_type) override;
|
||||
Address &address, Value::ValueType &value_type,
|
||||
llvm::ArrayRef<uint8_t> &local_buffer) override;
|
||||
|
||||
TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
|
||||
ValueObject &static_value) override;
|
||||
|
||||
@ -48,7 +48,7 @@ AppleObjCRuntimeV1::AppleObjCRuntimeV1(Process *process)
|
||||
bool AppleObjCRuntimeV1::GetDynamicTypeAndAddress(
|
||||
ValueObject &in_value, lldb::DynamicValueType use_dynamic,
|
||||
TypeAndOrName &class_type_or_name, Address &address,
|
||||
Value::ValueType &value_type) {
|
||||
Value::ValueType &value_type, llvm::ArrayRef<uint8_t> &local_buffer) {
|
||||
class_type_or_name.Clear();
|
||||
value_type = Value::ValueType::Scalar;
|
||||
if (CouldHaveDynamicValue(in_value)) {
|
||||
|
||||
@ -100,8 +100,8 @@ public:
|
||||
bool GetDynamicTypeAndAddress(ValueObject &in_value,
|
||||
lldb::DynamicValueType use_dynamic,
|
||||
TypeAndOrName &class_type_or_name,
|
||||
Address &address,
|
||||
Value::ValueType &value_type) override;
|
||||
Address &address, Value::ValueType &value_type,
|
||||
llvm::ArrayRef<uint8_t> &local_buffer) override;
|
||||
|
||||
llvm::Expected<std::unique_ptr<UtilityFunction>>
|
||||
CreateObjectChecker(std::string, ExecutionContext &exe_ctx) override;
|
||||
|
||||
@ -770,7 +770,7 @@ AppleObjCRuntimeV2::GetPreferredLanguageRuntime(ValueObject &in_value) {
|
||||
bool AppleObjCRuntimeV2::GetDynamicTypeAndAddress(
|
||||
ValueObject &in_value, lldb::DynamicValueType use_dynamic,
|
||||
TypeAndOrName &class_type_or_name, Address &address,
|
||||
Value::ValueType &value_type) {
|
||||
Value::ValueType &value_type, llvm::ArrayRef<uint8_t> &local_buffer) {
|
||||
// We should never get here with a null process...
|
||||
assert(m_process != nullptr);
|
||||
|
||||
|
||||
@ -53,8 +53,8 @@ public:
|
||||
bool GetDynamicTypeAndAddress(ValueObject &in_value,
|
||||
lldb::DynamicValueType use_dynamic,
|
||||
TypeAndOrName &class_type_or_name,
|
||||
Address &address,
|
||||
Value::ValueType &value_type) override;
|
||||
Address &address, Value::ValueType &value_type,
|
||||
llvm::ArrayRef<uint8_t> &local_buffer) override;
|
||||
|
||||
llvm::Expected<std::unique_ptr<UtilityFunction>>
|
||||
CreateObjectChecker(std::string name, ExecutionContext &exe_ctx) override;
|
||||
|
||||
@ -127,7 +127,7 @@ bool GNUstepObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) {
|
||||
bool GNUstepObjCRuntime::GetDynamicTypeAndAddress(
|
||||
ValueObject &in_value, DynamicValueType use_dynamic,
|
||||
TypeAndOrName &class_type_or_name, Address &address,
|
||||
Value::ValueType &value_type) {
|
||||
Value::ValueType &value_type, llvm::ArrayRef<uint8_t> &local_buffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -67,8 +67,8 @@ public:
|
||||
bool GetDynamicTypeAndAddress(ValueObject &in_value,
|
||||
lldb::DynamicValueType use_dynamic,
|
||||
TypeAndOrName &class_type_or_name,
|
||||
Address &address,
|
||||
Value::ValueType &value_type) override;
|
||||
Address &address, Value::ValueType &value_type,
|
||||
llvm::ArrayRef<uint8_t> &local_buffer) override;
|
||||
|
||||
TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
|
||||
ValueObject &static_value) override;
|
||||
|
||||
@ -849,6 +849,22 @@ bool ValueObject::SetData(DataExtractor &data, Status &error) {
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t ValueObject::GetLocalBufferSize() {
|
||||
if (m_value.GetValueType() != Value::ValueType::HostAddress)
|
||||
return LLDB_INVALID_ADDRESS;
|
||||
auto start = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
||||
if (start == LLDB_INVALID_ADDRESS)
|
||||
return LLDB_INVALID_ADDRESS;
|
||||
// Does our pointer point to this value object's m_data buffer?
|
||||
if ((uint64_t)m_data.GetDataStart() == start)
|
||||
return m_data.GetByteSize();
|
||||
// Does our pointer point to the value's buffer?
|
||||
if ((uint64_t)m_value.GetBuffer().GetBytes() == start)
|
||||
return m_value.GetBuffer().GetByteSize();
|
||||
// Our pointer points to something else. We can't know what the size is.
|
||||
return LLDB_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
static bool CopyStringDataToBufferSP(const StreamString &source,
|
||||
lldb::WritableDataBufferSP &destination) {
|
||||
llvm::StringRef src = source.GetString();
|
||||
|
||||
@ -145,6 +145,7 @@ bool ValueObjectDynamicValue::UpdateValue() {
|
||||
Address dynamic_address;
|
||||
bool found_dynamic_type = false;
|
||||
Value::ValueType value_type;
|
||||
llvm::ArrayRef<uint8_t> local_buffer;
|
||||
|
||||
LanguageRuntime *runtime = nullptr;
|
||||
|
||||
@ -157,7 +158,7 @@ bool ValueObjectDynamicValue::UpdateValue() {
|
||||
// Try the preferred runtime first.
|
||||
found_dynamic_type = preferred_runtime->GetDynamicTypeAndAddress(
|
||||
*m_parent, m_use_dynamic, class_type_or_name, dynamic_address,
|
||||
value_type);
|
||||
value_type, local_buffer);
|
||||
if (found_dynamic_type)
|
||||
// Set the operative `runtime` for later use in this function.
|
||||
runtime = preferred_runtime;
|
||||
@ -166,20 +167,20 @@ bool ValueObjectDynamicValue::UpdateValue() {
|
||||
// Fallback to the runtime for `known_type`.
|
||||
found_dynamic_type = runtime->GetDynamicTypeAndAddress(
|
||||
*m_parent, m_use_dynamic, class_type_or_name, dynamic_address,
|
||||
value_type);
|
||||
value_type, local_buffer);
|
||||
} else {
|
||||
runtime = process->GetLanguageRuntime(lldb::eLanguageTypeC_plus_plus);
|
||||
if (runtime)
|
||||
found_dynamic_type = runtime->GetDynamicTypeAndAddress(
|
||||
*m_parent, m_use_dynamic, class_type_or_name, dynamic_address,
|
||||
value_type);
|
||||
value_type, local_buffer);
|
||||
|
||||
if (!found_dynamic_type) {
|
||||
runtime = process->GetLanguageRuntime(lldb::eLanguageTypeObjC);
|
||||
if (runtime)
|
||||
found_dynamic_type = runtime->GetDynamicTypeAndAddress(
|
||||
*m_parent, m_use_dynamic, class_type_or_name, dynamic_address,
|
||||
value_type);
|
||||
value_type, local_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -239,11 +240,29 @@ bool ValueObjectDynamicValue::UpdateValue() {
|
||||
if (m_address.IsValid())
|
||||
SetValueDidChange(true);
|
||||
|
||||
// We've moved, so we should be fine...
|
||||
m_address = dynamic_address;
|
||||
lldb::TargetSP target_sp(GetTargetSP());
|
||||
lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get());
|
||||
m_value.GetScalar() = load_address;
|
||||
// If we found a host address, and the dynamic type fits in the local buffer
|
||||
// that was found, point to thar buffer. Later on this function will copy
|
||||
// the buffer over.
|
||||
if (value_type == Value::ValueType::HostAddress && !local_buffer.empty()) {
|
||||
auto *exe_scope = exe_ctx.GetBestExecutionContextScope();
|
||||
// If we found a host address but it doesn't fit in the buffer, there's
|
||||
// nothing we can do.
|
||||
if (local_buffer.size() <
|
||||
m_dynamic_type_info.GetCompilerType().GetByteSize(exe_scope)) {
|
||||
SetValueIsValid(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_value.GetScalar() = (uint64_t)local_buffer.data();
|
||||
m_address = LLDB_INVALID_ADDRESS;
|
||||
} else {
|
||||
// Otherwise we have a legitimate address on the target. Point to the load
|
||||
// address.
|
||||
m_address = dynamic_address;
|
||||
lldb::TargetSP target_sp(GetTargetSP());
|
||||
lldb::addr_t load_address = m_address.GetLoadAddress(target_sp.get());
|
||||
m_value.GetScalar() = load_address;
|
||||
}
|
||||
}
|
||||
|
||||
if (runtime)
|
||||
@ -258,7 +277,11 @@ bool ValueObjectDynamicValue::UpdateValue() {
|
||||
LLDB_LOGF(log, "[%s %p] has a new dynamic type %s", GetName().GetCString(),
|
||||
static_cast<void *>(this), GetTypeName().GetCString());
|
||||
|
||||
if (m_address.IsValid() && m_dynamic_type_info) {
|
||||
// m_address could be invalid but we could still have a local buffer
|
||||
// containing the dynamic value.
|
||||
if ((m_address.IsValid() ||
|
||||
m_value.GetValueType() == Value::ValueType::HostAddress) &&
|
||||
m_dynamic_type_info) {
|
||||
// The variable value is in the Scalar value inside the m_value. We can
|
||||
// point our m_data right to it.
|
||||
m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get());
|
||||
|
||||
@ -21,20 +21,21 @@ inline clang::DeclarationName getDeclarationName(TypeSystemClang &ast,
|
||||
return ast.getASTContext().DeclarationNames.getIdentifier(&II);
|
||||
}
|
||||
|
||||
inline CompilerType createRecord(TypeSystemClang &ast, llvm::StringRef name) {
|
||||
inline CompilerType
|
||||
createRecord(TypeSystemClang &ast, llvm::StringRef name,
|
||||
lldb::LanguageType lang = lldb::LanguageType::eLanguageTypeC) {
|
||||
return ast.CreateRecordType(ast.getASTContext().getTranslationUnitDecl(),
|
||||
OptionalClangModuleID(),
|
||||
lldb::AccessType::eAccessPublic, name, 0,
|
||||
lldb::LanguageType::eLanguageTypeC);
|
||||
lldb::AccessType::eAccessPublic, name, 0, lang);
|
||||
}
|
||||
|
||||
/// Create a record with the given name and a field with the given type
|
||||
/// and name.
|
||||
inline CompilerType createRecordWithField(TypeSystemClang &ast,
|
||||
llvm::StringRef record_name,
|
||||
CompilerType field_type,
|
||||
llvm::StringRef field_name) {
|
||||
CompilerType t = createRecord(ast, record_name);
|
||||
inline CompilerType createRecordWithField(
|
||||
TypeSystemClang &ast, llvm::StringRef record_name, CompilerType field_type,
|
||||
llvm::StringRef field_name,
|
||||
lldb::LanguageType lang = lldb::LanguageType::eLanguageTypeC) {
|
||||
CompilerType t = createRecord(ast, record_name, lang);
|
||||
|
||||
TypeSystemClang::StartTagDeclarationDefinition(t);
|
||||
ast.AddFieldToRecordType(t, field_name, field_type,
|
||||
@ -63,12 +64,13 @@ struct SourceASTWithRecord {
|
||||
CompilerType record_type;
|
||||
clang::RecordDecl *record_decl = nullptr;
|
||||
clang::FieldDecl *field_decl = nullptr;
|
||||
SourceASTWithRecord() {
|
||||
SourceASTWithRecord(
|
||||
lldb::LanguageType lang = lldb::LanguageType::eLanguageTypeC) {
|
||||
holder = std::make_unique<TypeSystemClangHolder>("test ASTContext");
|
||||
ast = holder->GetAST();
|
||||
record_type = createRecordWithField(
|
||||
*ast, "Source", ast->GetBasicType(lldb::BasicType::eBasicTypeChar),
|
||||
"a_field");
|
||||
"a_field", lang);
|
||||
record_decl =
|
||||
llvm::cast<clang::RecordDecl>(ClangUtil::GetAsTagDecl(record_type));
|
||||
field_decl = *record_decl->fields().begin();
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
add_lldb_unittest(LLDBValueObjectTests
|
||||
DumpValueObjectOptionsTests.cpp
|
||||
DILLexerTests.cpp
|
||||
DynamicValueObjectLocalBuffer.cpp
|
||||
|
||||
LINK_LIBS
|
||||
lldbValueObject
|
||||
|
||||
243
lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp
Normal file
243
lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp
Normal file
@ -0,0 +1,243 @@
|
||||
//===---DynamicValueObjectLocalBuffer.cpp-----------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "Plugins/Platform/Linux/PlatformLinux.h"
|
||||
#include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h"
|
||||
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
|
||||
#include "TestingSupport/SubsystemRAII.h"
|
||||
#include "TestingSupport/Symbol/ClangTestUtils.h"
|
||||
#include "lldb/Core/Debugger.h"
|
||||
#include "lldb/Core/PluginManager.h"
|
||||
#include "lldb/Target/Language.h"
|
||||
#include "lldb/Target/LanguageRuntime.h"
|
||||
#include "lldb/ValueObject/ValueObject.h"
|
||||
#include "lldb/ValueObject/ValueObjectConstResult.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
using namespace lldb_private::clang_utils;
|
||||
|
||||
// This entire class is boilerplate.
|
||||
struct MockLanguage : public Language {
|
||||
|
||||
llvm::StringRef GetPluginName() override { return "MockLanguage"; }
|
||||
lldb::LanguageType GetLanguageType() const override {
|
||||
return lldb::eLanguageTypeC_plus_plus;
|
||||
};
|
||||
|
||||
static Language *CreateInstance(lldb::LanguageType language) {
|
||||
return new MockLanguage();
|
||||
}
|
||||
static void Initialize() {
|
||||
PluginManager::RegisterPlugin("MockLanguage", "Mock Language",
|
||||
CreateInstance);
|
||||
};
|
||||
|
||||
static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); }
|
||||
bool IsSourceFile(llvm::StringRef file_path) const override { return true; }
|
||||
};
|
||||
LLDB_PLUGIN_DEFINE(MockLanguage)
|
||||
|
||||
struct MockLanguageRuntime : public LanguageRuntime {
|
||||
// This is the only method in this class that matters for this test.
|
||||
// This will unconditionally succeed and return a type with size 4,
|
||||
// a value_type of HostAddress, and a local buffer that points to the parent's
|
||||
// local buffer.
|
||||
// The tests will set that buffer to be either be larger or smaller than the
|
||||
// type we're returning.
|
||||
bool
|
||||
GetDynamicTypeAndAddress(ValueObject &in_value,
|
||||
lldb::DynamicValueType use_dynamic,
|
||||
TypeAndOrName &class_type_or_name, Address &address,
|
||||
Value::ValueType &value_type,
|
||||
llvm::ArrayRef<uint8_t> &local_buffer) override {
|
||||
auto ast = in_value.GetCompilerType()
|
||||
.GetTypeSystem()
|
||||
.dyn_cast_or_null<TypeSystemClang>();
|
||||
|
||||
auto int_type = createRecordWithField(
|
||||
*ast, "TypeWitInt", ast->GetBasicType(lldb::BasicType::eBasicTypeInt),
|
||||
"theIntField", LanguageType::eLanguageTypeC_plus_plus);
|
||||
class_type_or_name.SetCompilerType(int_type);
|
||||
local_buffer = {(uint8_t *)in_value.GetValue().GetScalar().ULongLong(
|
||||
LLDB_INVALID_ADDRESS),
|
||||
in_value.GetLocalBufferSize()};
|
||||
value_type = Value::ValueType::HostAddress;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// All of this is boilerplate.
|
||||
MockLanguageRuntime(Process *process) : LanguageRuntime(process) {}
|
||||
llvm::StringRef GetPluginName() override { return "MockLanguageRuntime"; }
|
||||
lldb::LanguageType GetLanguageType() const override {
|
||||
return lldb::eLanguageTypeC_plus_plus;
|
||||
}
|
||||
|
||||
llvm::Error GetObjectDescription(Stream &str, ValueObject &object) override {
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
llvm::Error GetObjectDescription(Stream &str, Value &value,
|
||||
ExecutionContextScope *exe_scope) override {
|
||||
return llvm::Error::success();
|
||||
}
|
||||
|
||||
bool CouldHaveDynamicValue(ValueObject &in_value) override { return true; }
|
||||
|
||||
TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
|
||||
ValueObject &static_value) override {
|
||||
return type_and_or_name;
|
||||
}
|
||||
|
||||
lldb::BreakpointResolverSP
|
||||
CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp,
|
||||
bool throw_bp) override {
|
||||
return lldb::BreakpointResolverSP();
|
||||
}
|
||||
|
||||
lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
|
||||
bool stop_others) override {
|
||||
return {};
|
||||
}
|
||||
|
||||
static LanguageRuntime *CreateInstance(Process *process,
|
||||
LanguageType language) {
|
||||
return new MockLanguageRuntime(process);
|
||||
}
|
||||
|
||||
static void Initialize() {
|
||||
PluginManager::RegisterPlugin(
|
||||
"MockLanguageRuntime", "MockLanguageRuntime", CreateInstance,
|
||||
[](CommandInterpreter &interpreter) -> lldb::CommandObjectSP {
|
||||
return {};
|
||||
},
|
||||
[](lldb::LanguageType language,
|
||||
bool throw_bp) -> BreakpointPreconditionSP { return {}; });
|
||||
}
|
||||
|
||||
static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); }
|
||||
};
|
||||
LLDB_PLUGIN_DEFINE(MockLanguageRuntime)
|
||||
|
||||
// This entire class is boilerplate.
|
||||
struct MockProcess : Process {
|
||||
MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
|
||||
: Process(target_sp, listener_sp) {}
|
||||
|
||||
llvm::StringRef GetPluginName() override { return "mock process"; }
|
||||
|
||||
bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override {
|
||||
return false;
|
||||
};
|
||||
|
||||
Status DoDestroy() override { return {}; }
|
||||
|
||||
void RefreshStateAfterStop() override {}
|
||||
|
||||
bool DoUpdateThreadList(ThreadList &old_thread_list,
|
||||
ThreadList &new_thread_list) override {
|
||||
return false;
|
||||
};
|
||||
|
||||
size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
|
||||
Status &error) override {
|
||||
// No need to read memory in these tests.
|
||||
return size;
|
||||
}
|
||||
};
|
||||
|
||||
class DynamicValueObjectLocalBufferTest : public ::testing::Test {
|
||||
public:
|
||||
void SetUp() override {
|
||||
ArchSpec arch("i386-pc-linux");
|
||||
Platform::SetHostPlatform(
|
||||
platform_linux::PlatformLinux::CreateInstance(true, &arch));
|
||||
// std::call_once(TestUtilities::g_debugger_initialize_flag,
|
||||
// []() { Debugger::Initialize(nullptr); });
|
||||
m_debugger_sp = Debugger::CreateInstance();
|
||||
ASSERT_TRUE(m_debugger_sp);
|
||||
m_debugger_sp->GetTargetList().CreateTarget(*m_debugger_sp, "", arch,
|
||||
eLoadDependentsNo,
|
||||
m_platform_sp, m_target_sp);
|
||||
ASSERT_TRUE(m_target_sp);
|
||||
ASSERT_TRUE(m_target_sp->GetArchitecture().IsValid());
|
||||
ASSERT_TRUE(m_platform_sp);
|
||||
m_listener_sp = Listener::MakeListener("dummy");
|
||||
m_process_sp = std::make_shared<MockProcess>(m_target_sp, m_listener_sp);
|
||||
ASSERT_TRUE(m_process_sp);
|
||||
m_exe_ctx = ExecutionContext(m_process_sp);
|
||||
|
||||
m_holder = std::make_unique<clang_utils::TypeSystemClangHolder>("test");
|
||||
m_type_system = m_holder->GetAST();
|
||||
LLDB_PLUGIN_INITIALIZE(MockLanguage);
|
||||
LLDB_PLUGIN_INITIALIZE(MockLanguageRuntime);
|
||||
}
|
||||
void TearDown() override {
|
||||
LLDB_PLUGIN_TERMINATE(MockLanguage);
|
||||
LLDB_PLUGIN_TERMINATE(MockLanguageRuntime);
|
||||
}
|
||||
|
||||
void TestValueObjectWithLocalBuffer(DataExtractor &data_extractor,
|
||||
bool should_succeed) {
|
||||
std::unique_ptr<TypeSystemClangHolder> holder =
|
||||
std::make_unique<TypeSystemClangHolder>("test ASTContext");
|
||||
TypeSystemClang *ast = holder->GetAST();
|
||||
auto char_type = createRecordWithField(
|
||||
*ast, "TypeWithChar",
|
||||
ast->GetBasicType(lldb::BasicType::eBasicTypeChar), "theField");
|
||||
|
||||
ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
|
||||
ConstString var_name("test_var");
|
||||
auto valobj_sp = ValueObjectConstResult::Create(exe_scope, char_type,
|
||||
var_name, data_extractor);
|
||||
auto dyn_valobj = valobj_sp->GetDynamicValue(lldb::eDynamicCanRunTarget);
|
||||
ASSERT_TRUE(dyn_valobj->GetValueIsValid() == should_succeed);
|
||||
}
|
||||
|
||||
SubsystemRAII<FileSystem, HostInfo, platform_linux::PlatformLinux,
|
||||
ScriptInterpreterNone>
|
||||
m_subsystems;
|
||||
std::unique_ptr<clang_utils::TypeSystemClangHolder> m_holder;
|
||||
lldb::DebuggerSP m_debugger_sp;
|
||||
lldb::TargetSP m_target_sp;
|
||||
lldb::PlatformSP m_platform_sp;
|
||||
lldb::ListenerSP m_listener_sp;
|
||||
lldb::ProcessSP m_process_sp;
|
||||
ExecutionContext m_exe_ctx;
|
||||
TypeSystemClang *m_type_system;
|
||||
};
|
||||
|
||||
TEST_F(DynamicValueObjectLocalBufferTest, BufferTooSmall) {
|
||||
/// Test that a value object with a buffer to small to fit the
|
||||
/// "dynamic" type will return an invalid dynamic value object.
|
||||
u_int8_t value = 1;
|
||||
ByteOrder endian = endian::InlHostByteOrder();
|
||||
DataExtractor data_extractor{&value, sizeof(value), endian, 4};
|
||||
TestValueObjectWithLocalBuffer(data_extractor, false);
|
||||
}
|
||||
|
||||
TEST_F(DynamicValueObjectLocalBufferTest, BufferTooBig) {
|
||||
/// Test that a value object with a buffer big enough fit the
|
||||
/// "dynamic" type will return a valid dynamic value object.
|
||||
uint64_t value = 1;
|
||||
ByteOrder endian = endian::InlHostByteOrder();
|
||||
DataExtractor data_extractor{&value, sizeof(value), endian, 4};
|
||||
TestValueObjectWithLocalBuffer(data_extractor, true);
|
||||
}
|
||||
|
||||
TEST_F(DynamicValueObjectLocalBufferTest, BufferExactlyRight) {
|
||||
/// Test that a value object with a buffer exactly the size of the
|
||||
/// "dynamic" type will return a valid dynamic value object.
|
||||
uint32_t value = 1;
|
||||
ByteOrder endian = endian::InlHostByteOrder();
|
||||
DataExtractor data_extractor{&value, sizeof(value), endian, 4};
|
||||
TestValueObjectWithLocalBuffer(data_extractor, true);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user