1 | //===-- DWARFIndexCachingTest.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/SymbolFile/DWARF/DIERef.h" |
10 | #include "Plugins/SymbolFile/DWARF/DWARFDIE.h" |
11 | #include "Plugins/SymbolFile/DWARF/ManualDWARFIndex.h" |
12 | #include "Plugins/SymbolFile/DWARF/NameToDIE.h" |
13 | #include "TestingSupport/Symbol/YAMLModuleTester.h" |
14 | #include "lldb/Core/DataFileCache.h" |
15 | #include "lldb/Core/ModuleList.h" |
16 | #include "lldb/Utility/DataEncoder.h" |
17 | #include "lldb/Utility/DataExtractor.h" |
18 | #include "llvm/ADT/STLExtras.h" |
19 | #include "gmock/gmock.h" |
20 | #include "gtest/gtest.h" |
21 | |
22 | using namespace lldb; |
23 | using namespace lldb_private; |
24 | using namespace lldb_private::plugin::dwarf; |
25 | |
26 | static void EncodeDecode(const DIERef &object, ByteOrder byte_order) { |
27 | const uint8_t addr_size = 8; |
28 | DataEncoder encoder(byte_order, addr_size); |
29 | object.Encode(encoder); |
30 | llvm::ArrayRef<uint8_t> bytes = encoder.GetData(); |
31 | DataExtractor data(bytes.data(), bytes.size(), byte_order, addr_size); |
32 | offset_t data_offset = 0; |
33 | EXPECT_EQ(object, DIERef::Decode(data, &data_offset)); |
34 | } |
35 | |
36 | static void EncodeDecode(const DIERef &object) { |
37 | EncodeDecode(object, byte_order: eByteOrderLittle); |
38 | EncodeDecode(object, byte_order: eByteOrderBig); |
39 | } |
40 | |
41 | TEST(DWARFIndexCachingTest, DIERefEncodeDecode) { |
42 | // Tests DIERef::Encode(...) and DIERef::Decode(...) |
43 | EncodeDecode(object: DIERef(std::nullopt, DIERef::Section::DebugInfo, 0x11223344)); |
44 | EncodeDecode(object: DIERef(std::nullopt, DIERef::Section::DebugTypes, 0x11223344)); |
45 | EncodeDecode(object: DIERef(100, DIERef::Section::DebugInfo, 0x11223344)); |
46 | EncodeDecode(object: DIERef(200, DIERef::Section::DebugTypes, 0x11223344)); |
47 | } |
48 | |
49 | TEST(DWARFIndexCachingTest, DIERefEncodeDecodeMax) { |
50 | // Tests DIERef::Encode(...) and DIERef::Decode(...) |
51 | EncodeDecode(object: DIERef(std::nullopt, DIERef::Section::DebugInfo, |
52 | DIERef::k_die_offset_mask - 1)); |
53 | EncodeDecode(object: DIERef(std::nullopt, DIERef::Section::DebugTypes, |
54 | DIERef::k_die_offset_mask - 1)); |
55 | EncodeDecode( |
56 | object: DIERef(100, DIERef::Section::DebugInfo, DIERef::k_die_offset_mask - 1)); |
57 | EncodeDecode( |
58 | object: DIERef(200, DIERef::Section::DebugTypes, DIERef::k_die_offset_mask - 1)); |
59 | EncodeDecode(object: DIERef(DIERef::k_file_index_mask, DIERef::Section::DebugInfo, |
60 | DIERef::k_file_index_mask)); |
61 | EncodeDecode(object: DIERef(DIERef::k_file_index_mask, DIERef::Section::DebugTypes, |
62 | DIERef::k_file_index_mask)); |
63 | EncodeDecode(object: DIERef(DIERef::k_file_index_mask, DIERef::Section::DebugInfo, |
64 | 0x11223344)); |
65 | EncodeDecode(object: DIERef(DIERef::k_file_index_mask, DIERef::Section::DebugTypes, |
66 | 0x11223344)); |
67 | } |
68 | |
69 | static void EncodeDecode(const NameToDIE &object, ByteOrder byte_order) { |
70 | const uint8_t addr_size = 8; |
71 | DataEncoder encoder(byte_order, addr_size); |
72 | DataEncoder strtab_encoder(byte_order, addr_size); |
73 | ConstStringTable const_strtab; |
74 | |
75 | object.Encode(encoder, strtab&: const_strtab); |
76 | |
77 | llvm::ArrayRef<uint8_t> bytes = encoder.GetData(); |
78 | DataExtractor data(bytes.data(), bytes.size(), byte_order, addr_size); |
79 | |
80 | const_strtab.Encode(encoder&: strtab_encoder); |
81 | llvm::ArrayRef<uint8_t> strtab_bytes = strtab_encoder.GetData(); |
82 | DataExtractor strtab_data(strtab_bytes.data(), strtab_bytes.size(), |
83 | byte_order, addr_size); |
84 | StringTableReader strtab_reader; |
85 | offset_t strtab_data_offset = 0; |
86 | ASSERT_EQ(strtab_reader.Decode(strtab_data, &strtab_data_offset), true); |
87 | |
88 | NameToDIE decoded_object; |
89 | offset_t data_offset = 0; |
90 | decoded_object.Decode(data, offset_ptr: &data_offset, strtab: strtab_reader); |
91 | EXPECT_EQ(object, decoded_object); |
92 | } |
93 | |
94 | static void EncodeDecode(const NameToDIE &object) { |
95 | EncodeDecode(object, byte_order: eByteOrderLittle); |
96 | EncodeDecode(object, byte_order: eByteOrderBig); |
97 | } |
98 | |
99 | TEST(DWARFIndexCachingTest, NameToDIEEncodeDecode) { |
100 | NameToDIE map; |
101 | // Make sure an empty NameToDIE map encodes and decodes correctly. |
102 | EncodeDecode(object: map); |
103 | map.Insert(name: ConstString("hello" ), |
104 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, 0x11223344)); |
105 | map.Insert(name: ConstString("workd" ), |
106 | die_ref: DIERef(100, DIERef::Section::DebugInfo, 0x11223344)); |
107 | map.Finalize(); |
108 | // Make sure a valid NameToDIE map encodes and decodes correctly. |
109 | EncodeDecode(object: map); |
110 | } |
111 | |
112 | static void EncodeDecode(const ManualDWARFIndex::IndexSet &object, |
113 | ByteOrder byte_order) { |
114 | const uint8_t addr_size = 8; |
115 | DataEncoder encoder(byte_order, addr_size); |
116 | DataEncoder strtab_encoder(byte_order, addr_size); |
117 | object.Encode(encoder); |
118 | llvm::ArrayRef<uint8_t> bytes = encoder.GetData(); |
119 | DataExtractor data(bytes.data(), bytes.size(), byte_order, addr_size); |
120 | ManualDWARFIndex::IndexSet decoded_object; |
121 | offset_t data_offset = 0; |
122 | decoded_object.Decode(data, offset_ptr: &data_offset); |
123 | EXPECT_TRUE(object == decoded_object); |
124 | } |
125 | |
126 | static void EncodeDecode(const ManualDWARFIndex::IndexSet &object) { |
127 | EncodeDecode(object, byte_order: eByteOrderLittle); |
128 | EncodeDecode(object, byte_order: eByteOrderBig); |
129 | } |
130 | |
131 | TEST(DWARFIndexCachingTest, ManualDWARFIndexIndexSetEncodeDecode) { |
132 | ManualDWARFIndex::IndexSet set; |
133 | // Make sure empty IndexSet can be encoded and decoded correctly |
134 | EncodeDecode(object: set); |
135 | |
136 | dw_offset_t die_offset = 0; |
137 | // Make sure an IndexSet with only items in IndexSet::function_basenames can |
138 | // be encoded and decoded correctly. |
139 | set.function_basenames.Insert( |
140 | name: ConstString("a" ), |
141 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
142 | EncodeDecode(object: set); |
143 | set.function_basenames.Clear(); |
144 | // Make sure an IndexSet with only items in IndexSet::function_fullnames can |
145 | // be encoded and decoded correctly. |
146 | set.function_fullnames.Insert( |
147 | name: ConstString("a" ), |
148 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
149 | EncodeDecode(object: set); |
150 | set.function_fullnames.Clear(); |
151 | // Make sure an IndexSet with only items in IndexSet::function_methods can |
152 | // be encoded and decoded correctly. |
153 | set.function_methods.Insert( |
154 | name: ConstString("a" ), |
155 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
156 | EncodeDecode(object: set); |
157 | set.function_methods.Clear(); |
158 | // Make sure an IndexSet with only items in IndexSet::function_selectors can |
159 | // be encoded and decoded correctly. |
160 | set.function_selectors.Insert( |
161 | name: ConstString("a" ), |
162 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
163 | EncodeDecode(object: set); |
164 | set.function_selectors.Clear(); |
165 | // Make sure an IndexSet with only items in IndexSet::objc_class_selectors can |
166 | // be encoded and decoded correctly. |
167 | set.objc_class_selectors.Insert( |
168 | name: ConstString("a" ), |
169 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
170 | EncodeDecode(object: set); |
171 | set.objc_class_selectors.Clear(); |
172 | // Make sure an IndexSet with only items in IndexSet::globals can |
173 | // be encoded and decoded correctly. |
174 | set.globals.Insert( |
175 | name: ConstString("a" ), |
176 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
177 | EncodeDecode(object: set); |
178 | set.globals.Clear(); |
179 | // Make sure an IndexSet with only items in IndexSet::types can |
180 | // be encoded and decoded correctly. |
181 | set.types.Insert( |
182 | name: ConstString("a" ), |
183 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
184 | EncodeDecode(object: set); |
185 | set.types.Clear(); |
186 | // Make sure an IndexSet with only items in IndexSet::namespaces can |
187 | // be encoded and decoded correctly. |
188 | set.namespaces.Insert( |
189 | name: ConstString("a" ), |
190 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
191 | EncodeDecode(object: set); |
192 | set.namespaces.Clear(); |
193 | // Make sure that an IndexSet with item in all NameToDIE maps can be |
194 | // be encoded and decoded correctly. |
195 | set.function_basenames.Insert( |
196 | name: ConstString("a" ), |
197 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
198 | set.function_fullnames.Insert( |
199 | name: ConstString("b" ), |
200 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
201 | set.function_methods.Insert( |
202 | name: ConstString("c" ), |
203 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
204 | set.function_selectors.Insert( |
205 | name: ConstString("d" ), |
206 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
207 | set.objc_class_selectors.Insert( |
208 | name: ConstString("e" ), |
209 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
210 | set.globals.Insert( |
211 | name: ConstString("f" ), |
212 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
213 | set.types.Insert( |
214 | name: ConstString("g" ), |
215 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
216 | set.namespaces.Insert( |
217 | name: ConstString("h" ), |
218 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
219 | EncodeDecode(object: set); |
220 | } |
221 | |
222 | static void EncodeDecode(const CacheSignature &object, ByteOrder byte_order, |
223 | bool encode_result) { |
224 | const uint8_t addr_size = 8; |
225 | DataEncoder encoder(byte_order, addr_size); |
226 | EXPECT_EQ(encode_result, object.Encode(encoder)); |
227 | if (!encode_result) |
228 | return; |
229 | llvm::ArrayRef<uint8_t> bytes = encoder.GetData(); |
230 | DataExtractor data(bytes.data(), bytes.size(), byte_order, addr_size); |
231 | offset_t data_offset = 0; |
232 | CacheSignature decoded_object; |
233 | EXPECT_TRUE(decoded_object.Decode(data, &data_offset)); |
234 | EXPECT_EQ(object, decoded_object); |
235 | } |
236 | |
237 | static void EncodeDecode(const CacheSignature &object, bool encode_result) { |
238 | EncodeDecode(object, byte_order: eByteOrderLittle, encode_result); |
239 | EncodeDecode(object, byte_order: eByteOrderBig, encode_result); |
240 | } |
241 | |
242 | TEST(DWARFIndexCachingTest, CacheSignatureTests) { |
243 | CacheSignature sig; |
244 | // A cache signature is only considered valid if it has a UUID. |
245 | sig.m_mod_time = 0x12345678; |
246 | EXPECT_FALSE(sig.IsValid()); |
247 | EncodeDecode(object: sig, /*encode_result=*/false); |
248 | sig.Clear(); |
249 | |
250 | sig.m_obj_mod_time = 0x12345678; |
251 | EXPECT_FALSE(sig.IsValid()); |
252 | EncodeDecode(object: sig, /*encode_result=*/false); |
253 | sig.Clear(); |
254 | |
255 | sig.m_uuid = UUID("@\x00\x11\x22\x33\x44\x55\x66\x77" , 8); |
256 | EXPECT_TRUE(sig.IsValid()); |
257 | EncodeDecode(object: sig, /*encode_result=*/true); |
258 | sig.m_mod_time = 0x12345678; |
259 | EXPECT_TRUE(sig.IsValid()); |
260 | EncodeDecode(object: sig, /*encode_result=*/true); |
261 | sig.m_obj_mod_time = 0x456789ab; |
262 | EXPECT_TRUE(sig.IsValid()); |
263 | EncodeDecode(object: sig, /*encode_result=*/true); |
264 | sig.m_mod_time = std::nullopt; |
265 | EXPECT_TRUE(sig.IsValid()); |
266 | EncodeDecode(object: sig, /*encode_result=*/true); |
267 | |
268 | // Recent changes do not allow cache signatures with only a modification time |
269 | // or object modification time, so make sure if we try to decode such a cache |
270 | // file that we fail. This verifies that if we try to load an previously |
271 | // valid cache file where the signature is insufficient, that we will fail to |
272 | // decode and load these cache files. |
273 | DataEncoder encoder(eByteOrderLittle, /*addr_size=*/8); |
274 | encoder.AppendU8(value: 2); // eSignatureModTime |
275 | encoder.AppendU32(value: 0x12345678); |
276 | encoder.AppendU8(value: 255); // eSignatureEnd |
277 | |
278 | llvm::ArrayRef<uint8_t> bytes = encoder.GetData(); |
279 | DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle, |
280 | /*addr_size=*/8); |
281 | offset_t data_offset = 0; |
282 | |
283 | // Make sure we fail to decode a CacheSignature with only a mod time |
284 | EXPECT_FALSE(sig.Decode(data, &data_offset)); |
285 | |
286 | // Change the signature data to contain only a eSignatureObjectModTime and |
287 | // make sure decoding fails as well. |
288 | encoder.PutU8(/*offset=*/0, value: 3); // eSignatureObjectModTime |
289 | data_offset = 0; |
290 | EXPECT_FALSE(sig.Decode(data, &data_offset)); |
291 | |
292 | } |
293 | |