1//===---DynamicValueObjectLocalBuffer.cpp-----------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Plugins/Platform/Linux/PlatformLinux.h"
10#include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h"
11#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
12#include "TestingSupport/SubsystemRAII.h"
13#include "TestingSupport/Symbol/ClangTestUtils.h"
14#include "lldb/Core/Debugger.h"
15#include "lldb/Core/PluginManager.h"
16#include "lldb/Target/Language.h"
17#include "lldb/Target/LanguageRuntime.h"
18#include "lldb/ValueObject/ValueObject.h"
19#include "lldb/ValueObject/ValueObjectConstResult.h"
20
21#include "gtest/gtest.h"
22
23using namespace lldb;
24using namespace lldb_private;
25using namespace lldb_private::clang_utils;
26
27// This entire class is boilerplate.
28struct MockLanguage : public Language {
29
30 llvm::StringRef GetPluginName() override { return "MockLanguage"; }
31 lldb::LanguageType GetLanguageType() const override {
32 return lldb::eLanguageTypeC_plus_plus;
33 };
34
35 static Language *CreateInstance(lldb::LanguageType language) {
36 return new MockLanguage();
37 }
38 static void Initialize() {
39 PluginManager::RegisterPlugin(name: "MockLanguage", description: "Mock Language",
40 create_callback: CreateInstance);
41 };
42
43 static void Terminate() { PluginManager::UnregisterPlugin(create_callback: CreateInstance); }
44 bool IsSourceFile(llvm::StringRef file_path) const override { return true; }
45};
46LLDB_PLUGIN_DEFINE(MockLanguage)
47
48struct MockLanguageRuntime : public LanguageRuntime {
49 // This is the only method in this class that matters for this test.
50 // This will unconditionally succeed and return a type with size 4,
51 // a value_type of HostAddress, and a local buffer that points to the parent's
52 // local buffer.
53 // The tests will set that buffer to be either be larger or smaller than the
54 // type we're returning.
55 bool
56 GetDynamicTypeAndAddress(ValueObject &in_value,
57 lldb::DynamicValueType use_dynamic,
58 TypeAndOrName &class_type_or_name, Address &address,
59 Value::ValueType &value_type,
60 llvm::ArrayRef<uint8_t> &local_buffer) override {
61 auto ast = in_value.GetCompilerType().GetTypeSystem<TypeSystemClang>();
62
63 auto int_type = createRecordWithField(
64 ast&: *ast, record_name: "TypeWitInt", field_type: ast->GetBasicType(type: lldb::BasicType::eBasicTypeInt),
65 field_name: "theIntField", lang: LanguageType::eLanguageTypeC_plus_plus);
66 class_type_or_name.SetCompilerType(int_type);
67 local_buffer = in_value.GetLocalBuffer();
68 value_type = Value::ValueType::HostAddress;
69 return true;
70 }
71
72 // All of this is boilerplate.
73 MockLanguageRuntime(Process *process) : LanguageRuntime(process) {}
74 llvm::StringRef GetPluginName() override { return "MockLanguageRuntime"; }
75 lldb::LanguageType GetLanguageType() const override {
76 return lldb::eLanguageTypeC_plus_plus;
77 }
78
79 llvm::Error GetObjectDescription(Stream &str, ValueObject &object) override {
80 return llvm::Error::success();
81 }
82
83 llvm::Error GetObjectDescription(Stream &str, Value &value,
84 ExecutionContextScope *exe_scope) override {
85 return llvm::Error::success();
86 }
87
88 bool CouldHaveDynamicValue(ValueObject &in_value) override { return true; }
89
90 TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
91 ValueObject &static_value) override {
92 return type_and_or_name;
93 }
94
95 lldb::BreakpointResolverSP
96 CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp,
97 bool throw_bp) override {
98 return lldb::BreakpointResolverSP();
99 }
100
101 lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
102 bool stop_others) override {
103 return {};
104 }
105
106 static LanguageRuntime *CreateInstance(Process *process,
107 LanguageType language) {
108 return new MockLanguageRuntime(process);
109 }
110
111 static void Initialize() {
112 PluginManager::RegisterPlugin(
113 name: "MockLanguageRuntime", description: "MockLanguageRuntime", create_callback: CreateInstance,
114 command_callback: [](CommandInterpreter &interpreter) -> lldb::CommandObjectSP {
115 return {};
116 },
117 precondition_callback: [](lldb::LanguageType language,
118 bool throw_bp) -> BreakpointPreconditionSP { return {}; });
119 }
120
121 static void Terminate() { PluginManager::UnregisterPlugin(create_callback: CreateInstance); }
122};
123LLDB_PLUGIN_DEFINE(MockLanguageRuntime)
124
125// This entire class is boilerplate.
126struct MockProcess : Process {
127 MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
128 : Process(target_sp, listener_sp) {}
129
130 llvm::StringRef GetPluginName() override { return "mock process"; }
131
132 bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override {
133 return false;
134 };
135
136 Status DoDestroy() override { return {}; }
137
138 void RefreshStateAfterStop() override {}
139
140 bool DoUpdateThreadList(ThreadList &old_thread_list,
141 ThreadList &new_thread_list) override {
142 return false;
143 };
144
145 size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
146 Status &error) override {
147 // No need to read memory in these tests.
148 return size;
149 }
150};
151
152class DynamicValueObjectLocalBufferTest : public ::testing::Test {
153public:
154 void SetUp() override {
155 ArchSpec arch("i386-pc-linux");
156 Platform::SetHostPlatform(
157 platform_linux::PlatformLinux::CreateInstance(force: true, arch: &arch));
158 // std::call_once(TestUtilities::g_debugger_initialize_flag,
159 // []() { Debugger::Initialize(nullptr); });
160 m_debugger_sp = Debugger::CreateInstance();
161 ASSERT_TRUE(m_debugger_sp);
162 m_debugger_sp->GetTargetList().CreateTarget(debugger&: *m_debugger_sp, user_exe_path: "", arch,
163 get_dependent_modules: eLoadDependentsNo,
164 platform_sp&: m_platform_sp, target_sp&: m_target_sp);
165 ASSERT_TRUE(m_target_sp);
166 ASSERT_TRUE(m_target_sp->GetArchitecture().IsValid());
167 ASSERT_TRUE(m_platform_sp);
168 m_listener_sp = Listener::MakeListener(name: "dummy");
169 m_process_sp = std::make_shared<MockProcess>(args&: m_target_sp, args&: m_listener_sp);
170 ASSERT_TRUE(m_process_sp);
171 m_exe_ctx = ExecutionContext(m_process_sp);
172
173 m_holder = std::make_unique<clang_utils::TypeSystemClangHolder>(args: "test");
174 m_type_system = m_holder->GetAST();
175 LLDB_PLUGIN_INITIALIZE(MockLanguage);
176 LLDB_PLUGIN_INITIALIZE(MockLanguageRuntime);
177 }
178 void TearDown() override {
179 LLDB_PLUGIN_TERMINATE(MockLanguage);
180 LLDB_PLUGIN_TERMINATE(MockLanguageRuntime);
181 }
182
183 void TestValueObjectWithLocalBuffer(DataExtractor &data_extractor,
184 bool should_succeed) {
185 std::unique_ptr<TypeSystemClangHolder> holder =
186 std::make_unique<TypeSystemClangHolder>(args: "test ASTContext");
187 TypeSystemClang *ast = holder->GetAST();
188 auto char_type = createRecordWithField(
189 ast&: *ast, record_name: "TypeWithChar",
190 field_type: ast->GetBasicType(type: lldb::BasicType::eBasicTypeChar), field_name: "theField");
191
192 ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
193 ConstString var_name("test_var");
194 auto valobj_sp = ValueObjectConstResult::Create(exe_scope, compiler_type: char_type,
195 name: var_name, data: data_extractor);
196 auto dyn_valobj = valobj_sp->GetDynamicValue(valueType: lldb::eDynamicCanRunTarget);
197 ASSERT_TRUE(dyn_valobj->GetValueIsValid() == should_succeed);
198 }
199
200 SubsystemRAII<FileSystem, HostInfo, platform_linux::PlatformLinux,
201 ScriptInterpreterNone>
202 m_subsystems;
203 std::unique_ptr<clang_utils::TypeSystemClangHolder> m_holder;
204 lldb::DebuggerSP m_debugger_sp;
205 lldb::TargetSP m_target_sp;
206 lldb::PlatformSP m_platform_sp;
207 lldb::ListenerSP m_listener_sp;
208 lldb::ProcessSP m_process_sp;
209 ExecutionContext m_exe_ctx;
210 TypeSystemClang *m_type_system;
211};
212
213TEST_F(DynamicValueObjectLocalBufferTest, BufferTooSmall) {
214 /// Test that a value object with a buffer to small to fit the
215 /// "dynamic" type will return an invalid dynamic value object.
216 uint8_t value = 1;
217 ByteOrder endian = endian::InlHostByteOrder();
218 DataExtractor data_extractor{&value, sizeof(value), endian, 4};
219 TestValueObjectWithLocalBuffer(data_extractor, should_succeed: false);
220}
221
222TEST_F(DynamicValueObjectLocalBufferTest, BufferTooBig) {
223 /// Test that a value object with a buffer big enough fit the
224 /// "dynamic" type will return a valid dynamic value object.
225 uint64_t value = 1;
226 ByteOrder endian = endian::InlHostByteOrder();
227 DataExtractor data_extractor{&value, sizeof(value), endian, 4};
228 TestValueObjectWithLocalBuffer(data_extractor, should_succeed: true);
229}
230
231TEST_F(DynamicValueObjectLocalBufferTest, BufferExactlyRight) {
232 /// Test that a value object with a buffer exactly the size of the
233 /// "dynamic" type will return a valid dynamic value object.
234 uint32_t value = 1;
235 ByteOrder endian = endian::InlHostByteOrder();
236 DataExtractor data_extractor{&value, sizeof(value), endian, 4};
237 TestValueObjectWithLocalBuffer(data_extractor, should_succeed: true);
238}
239

source code of lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp