blob: 417708dd2dc226c6a1d1248b50e7d77ca67a18c2 [file] [log] [blame]
Augusto Noronha0cbc4982025-02-07 03:04:011//===---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("MockLanguage", "Mock Language",
40 CreateInstance);
41 };
42
43 static void Terminate() { PluginManager::UnregisterPlugin(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()
62 .GetTypeSystem()
63 .dyn_cast_or_null<TypeSystemClang>();
64
65 auto int_type = createRecordWithField(
66 *ast, "TypeWitInt", ast->GetBasicType(lldb::BasicType::eBasicTypeInt),
67 "theIntField", LanguageType::eLanguageTypeC_plus_plus);
68 class_type_or_name.SetCompilerType(int_type);
Augusto Noronha9d5edc92025-02-08 03:12:3569 local_buffer = in_value.GetLocalBuffer();
Augusto Noronha0cbc4982025-02-07 03:04:0170 value_type = Value::ValueType::HostAddress;
Augusto Noronha0cbc4982025-02-07 03:04:0171 return true;
72 }
73
74 // All of this is boilerplate.
75 MockLanguageRuntime(Process *process) : LanguageRuntime(process) {}
76 llvm::StringRef GetPluginName() override { return "MockLanguageRuntime"; }
77 lldb::LanguageType GetLanguageType() const override {
78 return lldb::eLanguageTypeC_plus_plus;
79 }
80
81 llvm::Error GetObjectDescription(Stream &str, ValueObject &object) override {
82 return llvm::Error::success();
83 }
84
85 llvm::Error GetObjectDescription(Stream &str, Value &value,
86 ExecutionContextScope *exe_scope) override {
87 return llvm::Error::success();
88 }
89
90 bool CouldHaveDynamicValue(ValueObject &in_value) override { return true; }
91
92 TypeAndOrName FixUpDynamicType(const TypeAndOrName &type_and_or_name,
93 ValueObject &static_value) override {
94 return type_and_or_name;
95 }
96
97 lldb::BreakpointResolverSP
98 CreateExceptionResolver(const lldb::BreakpointSP &bkpt, bool catch_bp,
99 bool throw_bp) override {
100 return lldb::BreakpointResolverSP();
101 }
102
103 lldb::ThreadPlanSP GetStepThroughTrampolinePlan(Thread &thread,
104 bool stop_others) override {
105 return {};
106 }
107
108 static LanguageRuntime *CreateInstance(Process *process,
109 LanguageType language) {
110 return new MockLanguageRuntime(process);
111 }
112
113 static void Initialize() {
114 PluginManager::RegisterPlugin(
115 "MockLanguageRuntime", "MockLanguageRuntime", CreateInstance,
116 [](CommandInterpreter &interpreter) -> lldb::CommandObjectSP {
117 return {};
118 },
119 [](lldb::LanguageType language,
120 bool throw_bp) -> BreakpointPreconditionSP { return {}; });
121 }
122
123 static void Terminate() { PluginManager::UnregisterPlugin(CreateInstance); }
124};
125LLDB_PLUGIN_DEFINE(MockLanguageRuntime)
126
127// This entire class is boilerplate.
128struct MockProcess : Process {
129 MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp)
130 : Process(target_sp, listener_sp) {}
131
132 llvm::StringRef GetPluginName() override { return "mock process"; }
133
134 bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override {
135 return false;
136 };
137
138 Status DoDestroy() override { return {}; }
139
140 void RefreshStateAfterStop() override {}
141
142 bool DoUpdateThreadList(ThreadList &old_thread_list,
143 ThreadList &new_thread_list) override {
144 return false;
145 };
146
147 size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
148 Status &error) override {
149 // No need to read memory in these tests.
150 return size;
151 }
152};
153
154class DynamicValueObjectLocalBufferTest : public ::testing::Test {
155public:
156 void SetUp() override {
157 ArchSpec arch("i386-pc-linux");
158 Platform::SetHostPlatform(
159 platform_linux::PlatformLinux::CreateInstance(true, &arch));
160 // std::call_once(TestUtilities::g_debugger_initialize_flag,
161 // []() { Debugger::Initialize(nullptr); });
162 m_debugger_sp = Debugger::CreateInstance();
163 ASSERT_TRUE(m_debugger_sp);
164 m_debugger_sp->GetTargetList().CreateTarget(*m_debugger_sp, "", arch,
165 eLoadDependentsNo,
166 m_platform_sp, m_target_sp);
167 ASSERT_TRUE(m_target_sp);
168 ASSERT_TRUE(m_target_sp->GetArchitecture().IsValid());
169 ASSERT_TRUE(m_platform_sp);
170 m_listener_sp = Listener::MakeListener("dummy");
171 m_process_sp = std::make_shared<MockProcess>(m_target_sp, m_listener_sp);
172 ASSERT_TRUE(m_process_sp);
173 m_exe_ctx = ExecutionContext(m_process_sp);
174
175 m_holder = std::make_unique<clang_utils::TypeSystemClangHolder>("test");
176 m_type_system = m_holder->GetAST();
177 LLDB_PLUGIN_INITIALIZE(MockLanguage);
178 LLDB_PLUGIN_INITIALIZE(MockLanguageRuntime);
179 }
180 void TearDown() override {
181 LLDB_PLUGIN_TERMINATE(MockLanguage);
182 LLDB_PLUGIN_TERMINATE(MockLanguageRuntime);
183 }
184
185 void TestValueObjectWithLocalBuffer(DataExtractor &data_extractor,
186 bool should_succeed) {
187 std::unique_ptr<TypeSystemClangHolder> holder =
188 std::make_unique<TypeSystemClangHolder>("test ASTContext");
189 TypeSystemClang *ast = holder->GetAST();
190 auto char_type = createRecordWithField(
191 *ast, "TypeWithChar",
192 ast->GetBasicType(lldb::BasicType::eBasicTypeChar), "theField");
193
194 ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope();
195 ConstString var_name("test_var");
196 auto valobj_sp = ValueObjectConstResult::Create(exe_scope, char_type,
197 var_name, data_extractor);
198 auto dyn_valobj = valobj_sp->GetDynamicValue(lldb::eDynamicCanRunTarget);
199 ASSERT_TRUE(dyn_valobj->GetValueIsValid() == should_succeed);
200 }
201
202 SubsystemRAII<FileSystem, HostInfo, platform_linux::PlatformLinux,
203 ScriptInterpreterNone>
204 m_subsystems;
205 std::unique_ptr<clang_utils::TypeSystemClangHolder> m_holder;
206 lldb::DebuggerSP m_debugger_sp;
207 lldb::TargetSP m_target_sp;
208 lldb::PlatformSP m_platform_sp;
209 lldb::ListenerSP m_listener_sp;
210 lldb::ProcessSP m_process_sp;
211 ExecutionContext m_exe_ctx;
212 TypeSystemClang *m_type_system;
213};
214
215TEST_F(DynamicValueObjectLocalBufferTest, BufferTooSmall) {
216 /// Test that a value object with a buffer to small to fit the
217 /// "dynamic" type will return an invalid dynamic value object.
David Spickett9d837902025-02-07 10:03:23218 uint8_t value = 1;
Augusto Noronha0cbc4982025-02-07 03:04:01219 ByteOrder endian = endian::InlHostByteOrder();
220 DataExtractor data_extractor{&value, sizeof(value), endian, 4};
221 TestValueObjectWithLocalBuffer(data_extractor, false);
222}
223
224TEST_F(DynamicValueObjectLocalBufferTest, BufferTooBig) {
225 /// Test that a value object with a buffer big enough fit the
226 /// "dynamic" type will return a valid dynamic value object.
227 uint64_t value = 1;
228 ByteOrder endian = endian::InlHostByteOrder();
229 DataExtractor data_extractor{&value, sizeof(value), endian, 4};
230 TestValueObjectWithLocalBuffer(data_extractor, true);
231}
232
233TEST_F(DynamicValueObjectLocalBufferTest, BufferExactlyRight) {
234 /// Test that a value object with a buffer exactly the size of the
235 /// "dynamic" type will return a valid dynamic value object.
236 uint32_t value = 1;
237 ByteOrder endian = endian::InlHostByteOrder();
238 DataExtractor data_extractor{&value, sizeof(value), endian, 4};
239 TestValueObjectWithLocalBuffer(data_extractor, true);
240}