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/ManualDWARFIndexSet.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 IndexSet<NameToDIE> &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 | EncodeIndexSet(set: object, encoder); |
118 | llvm::ArrayRef<uint8_t> bytes = encoder.GetData(); |
119 | DataExtractor data(bytes.data(), bytes.size(), byte_order, addr_size); |
120 | offset_t data_offset = 0; |
121 | EXPECT_EQ(DecodeIndexSet(data, &data_offset), object); |
122 | } |
123 | |
124 | static void EncodeDecode(const IndexSet<NameToDIE> &object) { |
125 | EncodeDecode(object, byte_order: eByteOrderLittle); |
126 | EncodeDecode(object, byte_order: eByteOrderBig); |
127 | } |
128 | |
129 | TEST(DWARFIndexCachingTest, ManualDWARFIndexIndexSetEncodeDecode) { |
130 | IndexSet<NameToDIE> set; |
131 | // Make sure empty IndexSet can be encoded and decoded correctly |
132 | EncodeDecode(object: set); |
133 | |
134 | dw_offset_t die_offset = 0; |
135 | // Make sure an IndexSet with only items in IndexSet::function_basenames can |
136 | // be encoded and decoded correctly. |
137 | set.function_basenames.Insert( |
138 | name: ConstString("a" ), |
139 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
140 | EncodeDecode(object: set); |
141 | set.function_basenames.Clear(); |
142 | // Make sure an IndexSet with only items in IndexSet::function_fullnames can |
143 | // be encoded and decoded correctly. |
144 | set.function_fullnames.Insert( |
145 | name: ConstString("a" ), |
146 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
147 | EncodeDecode(object: set); |
148 | set.function_fullnames.Clear(); |
149 | // Make sure an IndexSet with only items in IndexSet::function_methods can |
150 | // be encoded and decoded correctly. |
151 | set.function_methods.Insert( |
152 | name: ConstString("a" ), |
153 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
154 | EncodeDecode(object: set); |
155 | set.function_methods.Clear(); |
156 | // Make sure an IndexSet with only items in IndexSet::function_selectors can |
157 | // be encoded and decoded correctly. |
158 | set.function_selectors.Insert( |
159 | name: ConstString("a" ), |
160 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
161 | EncodeDecode(object: set); |
162 | set.function_selectors.Clear(); |
163 | // Make sure an IndexSet with only items in IndexSet::objc_class_selectors can |
164 | // be encoded and decoded correctly. |
165 | set.objc_class_selectors.Insert( |
166 | name: ConstString("a" ), |
167 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
168 | EncodeDecode(object: set); |
169 | set.objc_class_selectors.Clear(); |
170 | // Make sure an IndexSet with only items in IndexSet::globals can |
171 | // be encoded and decoded correctly. |
172 | set.globals.Insert( |
173 | name: ConstString("a" ), |
174 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
175 | EncodeDecode(object: set); |
176 | set.globals.Clear(); |
177 | // Make sure an IndexSet with only items in IndexSet::types can |
178 | // be encoded and decoded correctly. |
179 | set.types.Insert( |
180 | name: ConstString("a" ), |
181 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
182 | EncodeDecode(object: set); |
183 | set.types.Clear(); |
184 | // Make sure an IndexSet with only items in IndexSet::namespaces can |
185 | // be encoded and decoded correctly. |
186 | set.namespaces.Insert( |
187 | name: ConstString("a" ), |
188 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
189 | EncodeDecode(object: set); |
190 | set.namespaces.Clear(); |
191 | // Make sure that an IndexSet with item in all NameToDIE maps can be |
192 | // be encoded and decoded correctly. |
193 | set.function_basenames.Insert( |
194 | name: ConstString("a" ), |
195 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
196 | set.function_fullnames.Insert( |
197 | name: ConstString("b" ), |
198 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
199 | set.function_methods.Insert( |
200 | name: ConstString("c" ), |
201 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
202 | set.function_selectors.Insert( |
203 | name: ConstString("d" ), |
204 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
205 | set.objc_class_selectors.Insert( |
206 | name: ConstString("e" ), |
207 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
208 | set.globals.Insert( |
209 | name: ConstString("f" ), |
210 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
211 | set.types.Insert( |
212 | name: ConstString("g" ), |
213 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
214 | set.namespaces.Insert( |
215 | name: ConstString("h" ), |
216 | die_ref: DIERef(std::nullopt, DIERef::Section::DebugInfo, ++die_offset)); |
217 | EncodeDecode(object: set); |
218 | } |
219 | |
220 | static void EncodeDecode(const CacheSignature &object, ByteOrder byte_order, |
221 | bool encode_result) { |
222 | const uint8_t addr_size = 8; |
223 | DataEncoder encoder(byte_order, addr_size); |
224 | EXPECT_EQ(encode_result, object.Encode(encoder)); |
225 | if (!encode_result) |
226 | return; |
227 | llvm::ArrayRef<uint8_t> bytes = encoder.GetData(); |
228 | DataExtractor data(bytes.data(), bytes.size(), byte_order, addr_size); |
229 | offset_t data_offset = 0; |
230 | CacheSignature decoded_object; |
231 | EXPECT_TRUE(decoded_object.Decode(data, &data_offset)); |
232 | EXPECT_EQ(object, decoded_object); |
233 | } |
234 | |
235 | static void EncodeDecode(const CacheSignature &object, bool encode_result) { |
236 | EncodeDecode(object, byte_order: eByteOrderLittle, encode_result); |
237 | EncodeDecode(object, byte_order: eByteOrderBig, encode_result); |
238 | } |
239 | |
240 | TEST(DWARFIndexCachingTest, CacheSignatureTests) { |
241 | CacheSignature sig; |
242 | // A cache signature is only considered valid if it has a UUID. |
243 | sig.m_mod_time = 0x12345678; |
244 | EXPECT_FALSE(sig.IsValid()); |
245 | EncodeDecode(object: sig, /*encode_result=*/false); |
246 | sig.Clear(); |
247 | |
248 | sig.m_obj_mod_time = 0x12345678; |
249 | EXPECT_FALSE(sig.IsValid()); |
250 | EncodeDecode(object: sig, /*encode_result=*/false); |
251 | sig.Clear(); |
252 | |
253 | sig.m_uuid = UUID("@\x00\x11\x22\x33\x44\x55\x66\x77" , 8); |
254 | EXPECT_TRUE(sig.IsValid()); |
255 | EncodeDecode(object: sig, /*encode_result=*/true); |
256 | sig.m_mod_time = 0x12345678; |
257 | EXPECT_TRUE(sig.IsValid()); |
258 | EncodeDecode(object: sig, /*encode_result=*/true); |
259 | sig.m_obj_mod_time = 0x456789ab; |
260 | EXPECT_TRUE(sig.IsValid()); |
261 | EncodeDecode(object: sig, /*encode_result=*/true); |
262 | sig.m_mod_time = std::nullopt; |
263 | EXPECT_TRUE(sig.IsValid()); |
264 | EncodeDecode(object: sig, /*encode_result=*/true); |
265 | |
266 | // Recent changes do not allow cache signatures with only a modification time |
267 | // or object modification time, so make sure if we try to decode such a cache |
268 | // file that we fail. This verifies that if we try to load an previously |
269 | // valid cache file where the signature is insufficient, that we will fail to |
270 | // decode and load these cache files. |
271 | DataEncoder encoder(eByteOrderLittle, /*addr_size=*/8); |
272 | encoder.AppendU8(value: 2); // eSignatureModTime |
273 | encoder.AppendU32(value: 0x12345678); |
274 | encoder.AppendU8(value: 255); // eSignatureEnd |
275 | |
276 | llvm::ArrayRef<uint8_t> bytes = encoder.GetData(); |
277 | DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle, |
278 | /*addr_size=*/8); |
279 | offset_t data_offset = 0; |
280 | |
281 | // Make sure we fail to decode a CacheSignature with only a mod time |
282 | EXPECT_FALSE(sig.Decode(data, &data_offset)); |
283 | |
284 | // Change the signature data to contain only a eSignatureObjectModTime and |
285 | // make sure decoding fails as well. |
286 | encoder.PutU8(/*offset=*/0, value: 3); // eSignatureObjectModTime |
287 | data_offset = 0; |
288 | EXPECT_FALSE(sig.Decode(data, &data_offset)); |
289 | |
290 | } |
291 | |