1 | //===-- DumpValueObjectOptionsTests.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/DataFormatters/DumpValueObjectOptions.h" |
16 | #include "lldb/ValueObject/ValueObject.h" |
17 | #include "lldb/ValueObject/ValueObjectConstResult.h" |
18 | |
19 | #include "gtest/gtest.h" |
20 | |
21 | #include <type_traits> |
22 | |
23 | using namespace lldb; |
24 | using namespace lldb_private; |
25 | |
26 | struct MockProcess : Process { |
27 | MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp) |
28 | : Process(target_sp, listener_sp) {} |
29 | |
30 | llvm::StringRef GetPluginName() override { return "mock process" ; } |
31 | |
32 | bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override { |
33 | return false; |
34 | }; |
35 | |
36 | Status DoDestroy() override { return {}; } |
37 | |
38 | void RefreshStateAfterStop() override {} |
39 | |
40 | bool DoUpdateThreadList(ThreadList &old_thread_list, |
41 | ThreadList &new_thread_list) override { |
42 | return false; |
43 | }; |
44 | |
45 | size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, |
46 | Status &error) override { |
47 | // No need to read memory in these tests. |
48 | return size; |
49 | } |
50 | }; |
51 | |
52 | class ValueObjectMockProcessTest : public ::testing::Test { |
53 | public: |
54 | void SetUp() override { |
55 | ArchSpec arch("i386-pc-linux" ); |
56 | Platform::SetHostPlatform( |
57 | platform_linux::PlatformLinux::CreateInstance(force: true, arch: &arch)); |
58 | m_debugger_sp = Debugger::CreateInstance(); |
59 | ASSERT_TRUE(m_debugger_sp); |
60 | m_debugger_sp->GetTargetList().CreateTarget(debugger&: *m_debugger_sp, user_exe_path: "" , arch, |
61 | get_dependent_modules: eLoadDependentsNo, |
62 | platform_sp&: m_platform_sp, target_sp&: m_target_sp); |
63 | ASSERT_TRUE(m_target_sp); |
64 | ASSERT_TRUE(m_target_sp->GetArchitecture().IsValid()); |
65 | ASSERT_TRUE(m_platform_sp); |
66 | m_listener_sp = Listener::MakeListener(name: "dummy" ); |
67 | m_process_sp = std::make_shared<MockProcess>(args&: m_target_sp, args&: m_listener_sp); |
68 | ASSERT_TRUE(m_process_sp); |
69 | m_exe_ctx = ExecutionContext(m_process_sp); |
70 | |
71 | m_holder = std::make_unique<clang_utils::TypeSystemClangHolder>(args: "test" ); |
72 | m_type_system = m_holder->GetAST(); |
73 | } |
74 | |
75 | template <typename UnderlyingType> |
76 | void TestDumpEnum( |
77 | const std::vector<std::pair<const char *, UnderlyingType>> enumerators, |
78 | const std::vector<std::tuple<UnderlyingType, DumpValueObjectOptions, |
79 | const char *>> &tests) { |
80 | CompilerType enum_type = MakeEnumType(enumerators); |
81 | StreamString strm; |
82 | ConstString var_name("test_var" ); |
83 | ByteOrder endian = endian::InlHostByteOrder(); |
84 | ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope(); |
85 | for (auto [value, options, expected] : tests) { |
86 | DataExtractor {&value, sizeof(value), endian, 4}; |
87 | auto valobj_sp = ValueObjectConstResult::Create(exe_scope, compiler_type: enum_type, |
88 | name: var_name, data: data_extractor); |
89 | if (llvm::Error error = valobj_sp->Dump(strm, options)) |
90 | llvm::consumeError(Err: std::move(error)); |
91 | ASSERT_STREQ(strm.GetString().str().c_str(), expected); |
92 | strm.Clear(); |
93 | } |
94 | } |
95 | |
96 | template <typename UnderlyingType> |
97 | CompilerType MakeEnumType( |
98 | const std::vector<std::pair<const char *, UnderlyingType>> enumerators) { |
99 | CompilerType int_type = m_type_system->GetBuiltinTypeForEncodingAndBitSize( |
100 | encoding: std::is_same<UnderlyingType, int>::value ? lldb::eEncodingSint |
101 | : lldb::eEncodingUint, |
102 | bit_size: 32); |
103 | CompilerType enum_type = m_type_system->CreateEnumerationType( |
104 | "TestEnum" , m_type_system->GetTranslationUnitDecl(), |
105 | OptionalClangModuleID(), Declaration(), int_type, false); |
106 | |
107 | m_type_system->StartTagDeclarationDefinition(type: enum_type); |
108 | Declaration decl; |
109 | for (auto [name, value] : enumerators) |
110 | m_type_system->AddEnumerationValueToEnumerationType(enum_type, decl, name, |
111 | value, 32); |
112 | m_type_system->CompleteTagDeclarationDefinition(type: enum_type); |
113 | |
114 | return enum_type; |
115 | } |
116 | |
117 | ExecutionContext m_exe_ctx; |
118 | TypeSystemClang *m_type_system; |
119 | |
120 | private: |
121 | SubsystemRAII<FileSystem, HostInfo, platform_linux::PlatformLinux, |
122 | ScriptInterpreterNone> |
123 | m_subsystems; |
124 | |
125 | std::unique_ptr<clang_utils::TypeSystemClangHolder> m_holder; |
126 | lldb::DebuggerSP m_debugger_sp; |
127 | lldb::TargetSP m_target_sp; |
128 | lldb::PlatformSP m_platform_sp; |
129 | lldb::ListenerSP m_listener_sp; |
130 | lldb::ProcessSP m_process_sp; |
131 | }; |
132 | |
133 | TEST_F(ValueObjectMockProcessTest, EmptyEnum) { |
134 | // All values of an empty enum should be shown as plain numbers. |
135 | TestDumpEnum<unsigned>(enumerators: {}, tests: {{0, {}, "(TestEnum) test_var = 0\n" }, |
136 | {1, {}, "(TestEnum) test_var = 1\n" }, |
137 | {2, {}, "(TestEnum) test_var = 2\n" }}); |
138 | |
139 | TestDumpEnum<int>(enumerators: {}, tests: {{-2, {}, "(TestEnum) test_var = -2\n" }, |
140 | {-1, {}, "(TestEnum) test_var = -1\n" }, |
141 | {0, {}, "(TestEnum) test_var = 0\n" }, |
142 | {1, {}, "(TestEnum) test_var = 1\n" }, |
143 | {2, {}, "(TestEnum) test_var = 2\n" }}); |
144 | } |
145 | |
146 | TEST_F(ValueObjectMockProcessTest, Enum) { |
147 | // This is not a bitfield-like enum, so values are printed as decimal by |
148 | // default. Also we only show the enumerator name if the value is an |
149 | // exact match. |
150 | TestDumpEnum<unsigned>( |
151 | enumerators: {{"test_2" , 2}, {"test_3" , 3}}, |
152 | tests: {{0, {}, "(TestEnum) test_var = 0\n" }, |
153 | {1, {}, "(TestEnum) test_var = 1\n" }, |
154 | {2, {}, "(TestEnum) test_var = test_2\n" }, |
155 | {3, {}, "(TestEnum) test_var = test_3\n" }, |
156 | {4, {}, "(TestEnum) test_var = 4\n" }, |
157 | {5, {}, "(TestEnum) test_var = 5\n" }, |
158 | {1, DumpValueObjectOptions().SetHideRootName(true), "(TestEnum) 1\n" }, |
159 | {1, DumpValueObjectOptions().SetHideRootType(true), "test_var = 1\n" }, |
160 | {1, DumpValueObjectOptions().SetHideRootName(true).SetHideRootType(true), |
161 | "1\n" }, |
162 | {1, DumpValueObjectOptions().SetHideName(true), "(TestEnum) 1\n" }, |
163 | {1, DumpValueObjectOptions().SetHideValue(true), |
164 | "(TestEnum) test_var =\n" }, |
165 | {1, DumpValueObjectOptions().SetHideName(true).SetHideValue(true), |
166 | "(TestEnum) \n" }}); |
167 | } |
168 | |
169 | TEST_F(ValueObjectMockProcessTest, BitFieldLikeEnum) { |
170 | // These enumerators set individual bits in the value, as if it were a flag |
171 | // set. lldb treats this as a "bitfield like enum". This means we show values |
172 | // as hex, and values without exact matches are shown as a combination of |
173 | // enumerators and any remaining value left over. |
174 | TestDumpEnum<unsigned>( |
175 | enumerators: {{"test_2" , 2}, {"test_4" , 4}}, |
176 | tests: { |
177 | {0, {}, "(TestEnum) test_var = 0x0\n" }, |
178 | {1, {}, "(TestEnum) test_var = 0x1\n" }, |
179 | {2, {}, "(TestEnum) test_var = test_2\n" }, |
180 | {4, {}, "(TestEnum) test_var = test_4\n" }, |
181 | {6, {}, "(TestEnum) test_var = test_2 | test_4\n" }, |
182 | {7, {}, "(TestEnum) test_var = test_2 | test_4 | 0x1\n" }, |
183 | {8, {}, "(TestEnum) test_var = 0x8\n" }, |
184 | {1, DumpValueObjectOptions().SetHideRootName(true), |
185 | "(TestEnum) 0x1\n" }, |
186 | {1, DumpValueObjectOptions().SetHideRootType(true), |
187 | "test_var = 0x1\n" }, |
188 | {1, |
189 | DumpValueObjectOptions().SetHideRootName(true).SetHideRootType(true), |
190 | "0x1\n" }, |
191 | {1, DumpValueObjectOptions().SetHideName(true), "(TestEnum) 0x1\n" }, |
192 | {1, DumpValueObjectOptions().SetHideValue(true), |
193 | "(TestEnum) test_var =\n" }, |
194 | {1, DumpValueObjectOptions().SetHideName(true).SetHideValue(true), |
195 | "(TestEnum) \n" }, |
196 | }); |
197 | } |
198 | |