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 | |
23 | using namespace lldb; |
24 | using namespace lldb_private; |
25 | using namespace lldb_private::clang_utils; |
26 | |
27 | // This entire class is boilerplate. |
28 | struct 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 | }; |
46 | LLDB_PLUGIN_DEFINE(MockLanguage) |
47 | |
48 | struct 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 | }; |
123 | LLDB_PLUGIN_DEFINE(MockLanguageRuntime) |
124 | |
125 | // This entire class is boilerplate. |
126 | struct 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 | |
152 | class DynamicValueObjectLocalBufferTest : public ::testing::Test { |
153 | public: |
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 (DataExtractor &, |
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 | |
213 | TEST_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 {&value, sizeof(value), endian, 4}; |
219 | TestValueObjectWithLocalBuffer(data_extractor, should_succeed: false); |
220 | } |
221 | |
222 | TEST_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 {&value, sizeof(value), endian, 4}; |
228 | TestValueObjectWithLocalBuffer(data_extractor, should_succeed: true); |
229 | } |
230 | |
231 | TEST_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 {&value, sizeof(value), endian, 4}; |
237 | TestValueObjectWithLocalBuffer(data_extractor, should_succeed: true); |
238 | } |
239 | |