/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Core/DataFileCache.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- DataFileCache.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 "lldb/Core/DataFileCache.h" |
10 | | #include "lldb/Core/Module.h" |
11 | | #include "lldb/Core/ModuleList.h" |
12 | | #include "lldb/Host/FileSystem.h" |
13 | | #include "lldb/Symbol/ObjectFile.h" |
14 | | #include "lldb/Utility/DataEncoder.h" |
15 | | #include "lldb/Utility/LLDBLog.h" |
16 | | #include "lldb/Utility/Log.h" |
17 | | #include "llvm/Support/CachePruning.h" |
18 | | |
19 | | using namespace lldb_private; |
20 | | |
21 | | |
22 | 4 | llvm::CachePruningPolicy DataFileCache::GetLLDBIndexCachePolicy() { |
23 | 4 | static llvm::CachePruningPolicy policy; |
24 | 4 | static llvm::once_flag once_flag; |
25 | | |
26 | 4 | llvm::call_once(once_flag, []() { |
27 | | // Prune the cache based off of the LLDB settings each time we create a |
28 | | // cache object. |
29 | 4 | ModuleListProperties &properties = |
30 | 4 | ModuleList::GetGlobalModuleListProperties(); |
31 | | // Only scan once an hour. If we have lots of debug sessions we don't want |
32 | | // to scan this directory too often. A timestamp file is written to the |
33 | | // directory to ensure different processes don't scan the directory too |
34 | | // often. This setting doesn't mean that a thread will continually scan the |
35 | | // cache directory within this process. |
36 | 4 | policy.Interval = std::chrono::hours(1); |
37 | | // Get the user settings for pruning. |
38 | 4 | policy.MaxSizeBytes = properties.GetLLDBIndexCacheMaxByteSize(); |
39 | 4 | policy.MaxSizePercentageOfAvailableSpace = |
40 | 4 | properties.GetLLDBIndexCacheMaxPercent(); |
41 | 4 | policy.Expiration = |
42 | 4 | std::chrono::hours(properties.GetLLDBIndexCacheExpirationDays() * 24); |
43 | 4 | }); |
44 | 4 | return policy; |
45 | 4 | } |
46 | | |
47 | 4 | DataFileCache::DataFileCache(llvm::StringRef path, llvm::CachePruningPolicy policy) { |
48 | 4 | m_cache_dir.SetPath(path); |
49 | 4 | pruneCache(path, policy); |
50 | | |
51 | | // This lambda will get called when the data is gotten from the cache and |
52 | | // also after the data was set for a given key. We only need to take |
53 | | // ownership of the data if we are geting the data, so we use the |
54 | | // m_take_ownership member variable to indicate if we need to take |
55 | | // ownership. |
56 | | |
57 | 4 | auto add_buffer = [this](unsigned task, const llvm::Twine &moduleName, |
58 | 8 | std::unique_ptr<llvm::MemoryBuffer> m) { |
59 | 8 | if (m_take_ownership) |
60 | 1 | m_mem_buff_up = std::move(m); |
61 | 8 | }; |
62 | 4 | llvm::Expected<llvm::FileCache> cache_or_err = |
63 | 4 | llvm::localCache("LLDBModuleCache", "lldb-module", path, add_buffer); |
64 | 4 | if (cache_or_err) |
65 | 4 | m_cache_callback = std::move(*cache_or_err); |
66 | 0 | else { |
67 | 0 | Log *log = GetLog(LLDBLog::Modules); |
68 | 0 | LLDB_LOG_ERROR(log, cache_or_err.takeError(), |
69 | 0 | "failed to create lldb index cache directory: {0}"); |
70 | 0 | } |
71 | 4 | } |
72 | | |
73 | | std::unique_ptr<llvm::MemoryBuffer> |
74 | 10 | DataFileCache::GetCachedData(llvm::StringRef key) { |
75 | 10 | std::lock_guard<std::mutex> guard(m_mutex); |
76 | | |
77 | 10 | const unsigned task = 1; |
78 | 10 | m_take_ownership = true; |
79 | | // If we call the "m_cache_callback" function and the data is cached, it will |
80 | | // call the "add_buffer" lambda function from the constructor which will in |
81 | | // turn take ownership of the member buffer that is passed to the callback and |
82 | | // put it into a member variable. |
83 | 10 | llvm::Expected<llvm::AddStreamFn> add_stream_or_err = |
84 | 10 | m_cache_callback(task, key, ""); |
85 | 10 | m_take_ownership = false; |
86 | | // At this point we either already called the "add_buffer" lambda with |
87 | | // the data or we haven't. We can tell if we got the cached data by checking |
88 | | // the add_stream function pointer value below. |
89 | 10 | if (add_stream_or_err) { |
90 | 10 | llvm::AddStreamFn &add_stream = *add_stream_or_err; |
91 | | // If the "add_stream" is nullptr, then the data was cached and we already |
92 | | // called the "add_buffer" lambda. If it is valid, then if we were to call |
93 | | // the add_stream function it would cause a cache file to get generated |
94 | | // and we would be expected to fill in the data. In this function we only |
95 | | // want to check if the data was cached, so we don't want to call |
96 | | // "add_stream" in this function. |
97 | 10 | if (!add_stream) |
98 | 1 | return std::move(m_mem_buff_up); |
99 | 10 | } else { |
100 | 0 | Log *log = GetLog(LLDBLog::Modules); |
101 | 0 | LLDB_LOG_ERROR(log, add_stream_or_err.takeError(), |
102 | 0 | "failed to get the cache add stream callback for key: {0}"); |
103 | 0 | } |
104 | | // Data was not cached. |
105 | 9 | return std::unique_ptr<llvm::MemoryBuffer>(); |
106 | 10 | } |
107 | | |
108 | | bool DataFileCache::SetCachedData(llvm::StringRef key, |
109 | 7 | llvm::ArrayRef<uint8_t> data) { |
110 | 7 | std::lock_guard<std::mutex> guard(m_mutex); |
111 | 7 | const unsigned task = 2; |
112 | | // If we call this function and the data is cached, it will call the |
113 | | // add_buffer lambda function from the constructor which will ignore the |
114 | | // data. |
115 | 7 | llvm::Expected<llvm::AddStreamFn> add_stream_or_err = |
116 | 7 | m_cache_callback(task, key, ""); |
117 | | // If we reach this code then we either already called the callback with |
118 | | // the data or we haven't. We can tell if we had the cached data by checking |
119 | | // the CacheAddStream function pointer value below. |
120 | 7 | if (add_stream_or_err) { |
121 | 7 | llvm::AddStreamFn &add_stream = *add_stream_or_err; |
122 | | // If the "add_stream" is nullptr, then the data was cached. If it is |
123 | | // valid, then if we call the add_stream function with a task it will |
124 | | // cause the file to get generated, but we only want to check if the data |
125 | | // is cached here, so we don't want to call it here. Note that the |
126 | | // add_buffer will also get called in this case after the data has been |
127 | | // provided, but we won't take ownership of the memory buffer as we just |
128 | | // want to write the data. |
129 | 7 | if (add_stream) { |
130 | 7 | llvm::Expected<std::unique_ptr<llvm::CachedFileStream>> file_or_err = |
131 | 7 | add_stream(task, ""); |
132 | 7 | if (file_or_err) { |
133 | 7 | llvm::CachedFileStream *cfs = file_or_err->get(); |
134 | 7 | cfs->OS->write((const char *)data.data(), data.size()); |
135 | 7 | return true; |
136 | 7 | } else { |
137 | 0 | Log *log = GetLog(LLDBLog::Modules); |
138 | 0 | LLDB_LOG_ERROR(log, file_or_err.takeError(), |
139 | 0 | "failed to get the cache file stream for key: {0}"); |
140 | 0 | } |
141 | 7 | } |
142 | 7 | } else { |
143 | 0 | Log *log = GetLog(LLDBLog::Modules); |
144 | 0 | LLDB_LOG_ERROR(log, add_stream_or_err.takeError(), |
145 | 0 | "failed to get the cache add stream callback for key: {0}"); |
146 | 0 | } |
147 | 0 | return false; |
148 | 7 | } |
149 | | |
150 | 1 | FileSpec DataFileCache::GetCacheFilePath(llvm::StringRef key) { |
151 | 1 | FileSpec cache_file(m_cache_dir); |
152 | 1 | std::string filename("llvmcache-"); |
153 | 1 | filename += key.str(); |
154 | 1 | cache_file.AppendPathComponent(filename); |
155 | 1 | return cache_file; |
156 | 1 | } |
157 | | |
158 | 1 | Status DataFileCache::RemoveCacheFile(llvm::StringRef key) { |
159 | 1 | FileSpec cache_file = GetCacheFilePath(key); |
160 | 1 | FileSystem &fs = FileSystem::Instance(); |
161 | 1 | if (!fs.Exists(cache_file)) |
162 | 0 | return Status(); |
163 | 1 | return fs.RemoveFile(cache_file); |
164 | 1 | } |
165 | | |
166 | 0 | CacheSignature::CacheSignature(lldb_private::Module *module) { |
167 | 0 | Clear(); |
168 | 0 | UUID uuid = module->GetUUID(); |
169 | 0 | if (uuid.IsValid()) |
170 | 0 | m_uuid = uuid; |
171 | |
|
172 | 0 | std::time_t mod_time = 0; |
173 | 0 | mod_time = llvm::sys::toTimeT(module->GetModificationTime()); |
174 | 0 | if (mod_time != 0) |
175 | 0 | m_mod_time = mod_time; |
176 | |
|
177 | 0 | mod_time = llvm::sys::toTimeT(module->GetObjectModificationTime()); |
178 | 0 | if (mod_time != 0) |
179 | 0 | m_obj_mod_time = mod_time; |
180 | 0 | } |
181 | | |
182 | 20 | CacheSignature::CacheSignature(lldb_private::ObjectFile *objfile) { |
183 | 20 | Clear(); |
184 | 20 | UUID uuid = objfile->GetUUID(); |
185 | 20 | if (uuid.IsValid()) |
186 | 17 | m_uuid = uuid; |
187 | | |
188 | 20 | std::time_t mod_time = 0; |
189 | | // Grab the modification time of the object file's file. It isn't always the |
190 | | // same as the module's file when you have a executable file as the main |
191 | | // executable, and you have a object file for a symbol file. |
192 | 20 | FileSystem &fs = FileSystem::Instance(); |
193 | 20 | mod_time = llvm::sys::toTimeT(fs.GetModificationTime(objfile->GetFileSpec())); |
194 | 20 | if (mod_time != 0) |
195 | 11 | m_mod_time = mod_time; |
196 | | |
197 | 20 | mod_time = |
198 | 20 | llvm::sys::toTimeT(objfile->GetModule()->GetObjectModificationTime()); |
199 | 20 | if (mod_time != 0) |
200 | 3 | m_obj_mod_time = mod_time; |
201 | 20 | } |
202 | | |
203 | | enum SignatureEncoding { |
204 | | eSignatureUUID = 1u, |
205 | | eSignatureModTime = 2u, |
206 | | eSignatureObjectModTime = 3u, |
207 | | eSignatureEnd = 255u, |
208 | | }; |
209 | | |
210 | 26 | bool CacheSignature::Encode(DataEncoder &encoder) const { |
211 | 26 | if (!IsValid()) |
212 | 7 | return false; // Invalid signature, return false! |
213 | | |
214 | 19 | if (m_uuid) { |
215 | 19 | llvm::ArrayRef<uint8_t> uuid_bytes = m_uuid->GetBytes(); |
216 | 19 | encoder.AppendU8(eSignatureUUID); |
217 | 19 | encoder.AppendU8(uuid_bytes.size()); |
218 | 19 | encoder.AppendData(uuid_bytes); |
219 | 19 | } |
220 | 19 | if (m_mod_time) { |
221 | 11 | encoder.AppendU8(eSignatureModTime); |
222 | 11 | encoder.AppendU32(*m_mod_time); |
223 | 11 | } |
224 | 19 | if (m_obj_mod_time) { |
225 | 4 | encoder.AppendU8(eSignatureObjectModTime); |
226 | 4 | encoder.AppendU32(*m_obj_mod_time); |
227 | 4 | } |
228 | 19 | encoder.AppendU8(eSignatureEnd); |
229 | 19 | return true; |
230 | 26 | } |
231 | | |
232 | | bool CacheSignature::Decode(const lldb_private::DataExtractor &data, |
233 | 16 | lldb::offset_t *offset_ptr) { |
234 | 16 | Clear(); |
235 | 41 | while (uint8_t sig_encoding = data.GetU8(offset_ptr)) { |
236 | 41 | switch (sig_encoding) { |
237 | 14 | case eSignatureUUID: { |
238 | 14 | const uint8_t length = data.GetU8(offset_ptr); |
239 | 14 | const uint8_t *bytes = (const uint8_t *)data.GetData(offset_ptr, length); |
240 | 14 | if (bytes != nullptr && length > 0) |
241 | 14 | m_uuid = UUID(llvm::ArrayRef<uint8_t>(bytes, length)); |
242 | 14 | } break; |
243 | 6 | case eSignatureModTime: { |
244 | 6 | uint32_t mod_time = data.GetU32(offset_ptr); |
245 | 6 | if (mod_time > 0) |
246 | 6 | m_mod_time = mod_time; |
247 | 6 | } break; |
248 | 5 | case eSignatureObjectModTime: { |
249 | 5 | uint32_t mod_time = data.GetU32(offset_ptr); |
250 | 5 | if (mod_time > 0) |
251 | 5 | m_obj_mod_time = mod_time; |
252 | 5 | } break; |
253 | 16 | case eSignatureEnd: |
254 | | // The definition of is valid changed to only be valid if the UUID is |
255 | | // valid so make sure that if we attempt to decode an old cache file |
256 | | // that we will fail to decode the cache file if the signature isn't |
257 | | // considered valid. |
258 | 16 | return IsValid(); |
259 | 0 | default: |
260 | 0 | break; |
261 | 41 | } |
262 | 41 | } |
263 | 0 | return false; |
264 | 16 | } |
265 | | |
266 | 180 | uint32_t ConstStringTable::Add(ConstString s) { |
267 | 180 | auto pos = m_string_to_offset.find(s); |
268 | 180 | if (pos != m_string_to_offset.end()) |
269 | 52 | return pos->second; |
270 | 128 | const uint32_t offset = m_next_offset; |
271 | 128 | m_strings.push_back(s); |
272 | 128 | m_string_to_offset[s] = offset; |
273 | 128 | m_next_offset += s.GetLength() + 1; |
274 | 128 | return offset; |
275 | 180 | } |
276 | | |
277 | | static const llvm::StringRef kStringTableIdentifier("STAB"); |
278 | | |
279 | 65 | bool ConstStringTable::Encode(DataEncoder &encoder) { |
280 | | // Write an 4 character code into the stream. This will help us when decoding |
281 | | // to make sure we find this identifier when decoding the string table to make |
282 | | // sure we have the rigth data. It also helps to identify the string table |
283 | | // when dumping the hex bytes in a cache file. |
284 | 65 | encoder.AppendData(kStringTableIdentifier); |
285 | 65 | size_t length_offset = encoder.GetByteSize(); |
286 | 65 | encoder.AppendU32(0); // Total length of all strings which will be fixed up. |
287 | 65 | size_t strtab_offset = encoder.GetByteSize(); |
288 | 65 | encoder.AppendU8(0); // Start the string table with an empty string. |
289 | 128 | for (auto s: m_strings) { |
290 | | // Make sure all of the offsets match up with what we handed out! |
291 | 128 | assert(m_string_to_offset.find(s)->second == |
292 | 128 | encoder.GetByteSize() - strtab_offset); |
293 | | // Append the C string into the encoder |
294 | 128 | encoder.AppendCString(s.GetStringRef()); |
295 | 128 | } |
296 | | // Fixup the string table length. |
297 | 65 | encoder.PutU32(length_offset, encoder.GetByteSize() - strtab_offset); |
298 | 65 | return true; |
299 | 65 | } |
300 | | |
301 | | bool StringTableReader::Decode(const lldb_private::DataExtractor &data, |
302 | 59 | lldb::offset_t *offset_ptr) { |
303 | 59 | llvm::StringRef identifier((const char *)data.GetData(offset_ptr, 4), 4); |
304 | 59 | if (identifier != kStringTableIdentifier) |
305 | 0 | return false; |
306 | 59 | const uint32_t length = data.GetU32(offset_ptr); |
307 | | // We always have at least one byte for the empty string at offset zero. |
308 | 59 | if (length == 0) |
309 | 0 | return false; |
310 | 59 | const char *bytes = (const char *)data.GetData(offset_ptr, length); |
311 | 59 | if (bytes == nullptr) |
312 | 0 | return false; |
313 | 59 | m_data = llvm::StringRef(bytes, length); |
314 | 59 | return true; |
315 | 59 | } |
316 | | |
317 | 96 | llvm::StringRef StringTableReader::Get(uint32_t offset) const { |
318 | 96 | if (offset >= m_data.size()) |
319 | 0 | return llvm::StringRef(); |
320 | 96 | return llvm::StringRef(m_data.data() + offset); |
321 | 96 | } |
322 | | |