Skip to content

Commit b6087ba

Browse files
committed
Disable LLDB index cache for .o files with no UUID.
After enabling the LLDB index cache in production we discovered that some distributed build systems play with the modification times of any .o files that were downloaded from the build cache. This was causing the LLDB index cache to read the wrong cache file for files that didn't have a UUID as all of the modfication times were set to the same value by the build system. When new .o files were downloaded, the only unique identifier was the mod time which were all the same, and we would load an older cache for the updated .o file. So disabling caching of files that have no UUIDs for now until we can create a more solid solution. Differential Revision: https://reviews.llvm.org/D120948
1 parent 30922d6 commit b6087ba

File tree

4 files changed

+126
-21
lines changed

4 files changed

+126
-21
lines changed

lldb/include/lldb/Core/DataFileCache.h

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -119,22 +119,22 @@ struct CacheSignature {
119119
m_obj_mod_time = llvm::None;
120120
}
121121

122-
/// Return true if any of the signature member variables have valid values.
123-
bool IsValid() const {
124-
return m_uuid.hasValue() || m_mod_time.hasValue() ||
125-
m_obj_mod_time.hasValue();
126-
}
122+
/// Return true only if the CacheSignature is valid.
123+
///
124+
/// Cache signatures are considered valid only if there is a UUID in the file
125+
/// that can uniquely identify the file. Some build systems play with
126+
/// modification times of file so we can not trust them without using valid
127+
/// unique idenifier like the UUID being valid.
128+
bool IsValid() const { return m_uuid.hasValue(); }
127129

128130
/// Check if two signatures are the same.
129-
bool operator!=(const CacheSignature &rhs) {
130-
if (m_uuid != rhs.m_uuid)
131-
return true;
132-
if (m_mod_time != rhs.m_mod_time)
133-
return true;
134-
if (m_obj_mod_time != rhs.m_obj_mod_time)
135-
return true;
136-
return false;
131+
bool operator==(const CacheSignature &rhs) const {
132+
return m_uuid == rhs.m_uuid && m_mod_time == rhs.m_mod_time &&
133+
m_obj_mod_time == rhs.m_obj_mod_time;
137134
}
135+
136+
/// Check if two signatures differ.
137+
bool operator!=(const CacheSignature &rhs) const { return !(*this == rhs); }
138138
/// Encode this object into a data encoder object.
139139
///
140140
/// This allows this object to be serialized to disk. The CacheSignature
@@ -149,7 +149,7 @@ struct CacheSignature {
149149
/// True if a signature was encoded, and false if there were no member
150150
/// variables that had value. False indicates this data should not be
151151
/// cached to disk because we were unable to encode a valid signature.
152-
bool Encode(DataEncoder &encoder);
152+
bool Encode(DataEncoder &encoder) const;
153153

154154
/// Decode a serialized version of this object from data.
155155
///

lldb/source/Core/DataFileCache.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ enum SignatureEncoding {
199199
eSignatureEnd = 255u,
200200
};
201201

202-
bool CacheSignature::Encode(DataEncoder &encoder) {
202+
bool CacheSignature::Encode(DataEncoder &encoder) const {
203203
if (!IsValid())
204204
return false; // Invalid signature, return false!
205205

@@ -240,10 +240,14 @@ bool CacheSignature::Decode(const lldb_private::DataExtractor &data,
240240
case eSignatureObjectModTime: {
241241
uint32_t mod_time = data.GetU32(offset_ptr);
242242
if (mod_time > 0)
243-
m_mod_time = mod_time;
243+
m_obj_mod_time = mod_time;
244244
} break;
245245
case eSignatureEnd:
246-
return true;
246+
// The definition of is valid changed to only be valid if the UUID is
247+
// valid so make sure that if we attempt to decode an old cache file
248+
// that we will fail to decode the cache file if the signature isn't
249+
// considered valid.
250+
return IsValid();
247251
default:
248252
break;
249253
}

lldb/test/API/functionalities/module_cache/bsd/TestModuleCacheBSD.py

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,25 @@ def get_module_cache_files(self, basename):
4141
@skipUnlessDarwin
4242
def test(self):
4343
"""
44+
This test has been modified to make sure .o files that don't have
45+
UUIDs are not cached after discovering build systems that play with
46+
modification times of .o files that the modification times are not
47+
unique enough to ensure the .o file within the .a file are the right
48+
files as this was causing older cache files to be accepted for new
49+
updated .o files.
50+
51+
ELF .o files do calculate a UUID from the contents of the file,
52+
which is expensive, but no one loads .o files into a debug sessions
53+
when using ELF files. Mach-o .o files do not have UUID values and do
54+
no calculate one as they _are_ used during debug sessions when no
55+
dSYM file is generated. If we can find a way to uniquely and cheaply
56+
create UUID values for mach-o .o files in the future, this test will
57+
be updated to test this functionality. This test will now make sure
58+
there are no cache entries for any .o files in BSD archives.
59+
60+
The old test case description is below in case we enable caching for
61+
.o files again:
62+
4463
Test module cache functionality for bsd archive object files.
4564
4665
This will test that if we enable the module cache, we have a
@@ -76,10 +95,20 @@ def test(self):
7695
main_module.GetNumSymbols()
7796
a_o_cache_files = self.get_module_cache_files("libfoo.a(a.o)")
7897
b_o_cache_files = self.get_module_cache_files("libfoo.a(b.o)")
98+
99+
79100
# We expect the directory for a.o to have two cache directories:
80101
# - 1 for the a.o with a earlier mod time
81102
# - 1 for the a.o that was renamed from c.o that should be 2 seconds older
82-
self.assertEqual(len(a_o_cache_files), 2,
83-
"make sure there are two files in the module cache directory (%s) for libfoo.a(a.o)" % (self.cache_dir))
84-
self.assertEqual(len(b_o_cache_files), 1,
85-
"make sure there are two files in the module cache directory (%s) for libfoo.a(b.o)" % (self.cache_dir))
103+
# self.assertEqual(len(a_o_cache_files), 2,
104+
# "make sure there are two files in the module cache directory (%s) for libfoo.a(a.o)" % (self.cache_dir))
105+
# self.assertEqual(len(b_o_cache_files), 1,
106+
# "make sure there are two files in the module cache directory (%s) for libfoo.a(b.o)" % (self.cache_dir))
107+
108+
# We are no longer caching .o files in the lldb index cache. If we ever
109+
# re-enable this functionality, we can uncomment out the above lines of
110+
# code.
111+
self.assertEqual(len(a_o_cache_files), 0,
112+
"make sure there are no files in the module cache directory (%s) for libfoo.a(a.o)" % (self.cache_dir))
113+
self.assertEqual(len(b_o_cache_files), 0,
114+
"make sure there are no files in the module cache directory (%s) for libfoo.a(b.o)" % (self.cache_dir))

lldb/unittests/SymbolFile/DWARF/DWARFIndexCachingTest.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,75 @@ TEST(DWARFIndexCachingTest, ManualDWARFIndexIndexSetEncodeDecode) {
196196
DIERef(llvm::None, DIERef::Section::DebugInfo, ++die_offset));
197197
EncodeDecode(set);
198198
}
199+
200+
static void EncodeDecode(const CacheSignature &object, ByteOrder byte_order,
201+
bool encode_result) {
202+
const uint8_t addr_size = 8;
203+
DataEncoder encoder(byte_order, addr_size);
204+
EXPECT_EQ(encode_result, object.Encode(encoder));
205+
if (!encode_result)
206+
return;
207+
llvm::ArrayRef<uint8_t> bytes = encoder.GetData();
208+
DataExtractor data(bytes.data(), bytes.size(), byte_order, addr_size);
209+
offset_t data_offset = 0;
210+
CacheSignature decoded_object;
211+
EXPECT_TRUE(decoded_object.Decode(data, &data_offset));
212+
EXPECT_EQ(object, decoded_object);
213+
}
214+
215+
static void EncodeDecode(const CacheSignature &object, bool encode_result) {
216+
EncodeDecode(object, eByteOrderLittle, encode_result);
217+
EncodeDecode(object, eByteOrderBig, encode_result);
218+
}
219+
220+
TEST(DWARFIndexCachingTest, CacheSignatureTests) {
221+
CacheSignature sig;
222+
// A cache signature is only considered valid if it has a UUID.
223+
sig.m_mod_time = 0x12345678;
224+
EXPECT_FALSE(sig.IsValid());
225+
EncodeDecode(sig, /*encode_result=*/false);
226+
sig.Clear();
227+
228+
sig.m_obj_mod_time = 0x12345678;
229+
EXPECT_FALSE(sig.IsValid());
230+
EncodeDecode(sig, /*encode_result=*/false);
231+
sig.Clear();
232+
233+
sig.m_uuid = UUID::fromData("@\x00\x11\x22\x33\x44\x55\x66\x77", 8);
234+
EXPECT_TRUE(sig.IsValid());
235+
EncodeDecode(sig, /*encode_result=*/true);
236+
sig.m_mod_time = 0x12345678;
237+
EXPECT_TRUE(sig.IsValid());
238+
EncodeDecode(sig, /*encode_result=*/true);
239+
sig.m_obj_mod_time = 0x456789ab;
240+
EXPECT_TRUE(sig.IsValid());
241+
EncodeDecode(sig, /*encode_result=*/true);
242+
sig.m_mod_time = llvm::None;
243+
EXPECT_TRUE(sig.IsValid());
244+
EncodeDecode(sig, /*encode_result=*/true);
245+
246+
// Recent changes do not allow cache signatures with only a modification time
247+
// or object modification time, so make sure if we try to decode such a cache
248+
// file that we fail. This verifies that if we try to load an previously
249+
// valid cache file where the signature is insufficient, that we will fail to
250+
// decode and load these cache files.
251+
DataEncoder encoder(eByteOrderLittle, /*addr_size=*/8);
252+
encoder.AppendU8(2); // eSignatureModTime
253+
encoder.AppendU32(0x12345678);
254+
encoder.AppendU8(255); // eSignatureEnd
255+
256+
llvm::ArrayRef<uint8_t> bytes = encoder.GetData();
257+
DataExtractor data(bytes.data(), bytes.size(), eByteOrderLittle,
258+
/*addr_size=*/8);
259+
offset_t data_offset = 0;
260+
261+
// Make sure we fail to decode a CacheSignature with only a mod time
262+
EXPECT_FALSE(sig.Decode(data, &data_offset));
263+
264+
// Change the signature data to contain only a eSignatureObjectModTime and
265+
// make sure decoding fails as well.
266+
encoder.PutU8(/*offset=*/0, 3); // eSignatureObjectModTime
267+
data_offset = 0;
268+
EXPECT_FALSE(sig.Decode(data, &data_offset));
269+
270+
}

0 commit comments

Comments
 (0)