Path: blob/main/contrib/llvm-project/lldb/source/Core/DataFileCache.cpp
39587 views
//===-- DataFileCache.cpp -------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Core/DataFileCache.h"9#include "lldb/Core/Module.h"10#include "lldb/Core/ModuleList.h"11#include "lldb/Host/FileSystem.h"12#include "lldb/Symbol/ObjectFile.h"13#include "lldb/Utility/DataEncoder.h"14#include "lldb/Utility/LLDBLog.h"15#include "lldb/Utility/Log.h"16#include "llvm/Support/CachePruning.h"1718using namespace lldb_private;192021llvm::CachePruningPolicy DataFileCache::GetLLDBIndexCachePolicy() {22static llvm::CachePruningPolicy policy;23static llvm::once_flag once_flag;2425llvm::call_once(once_flag, []() {26// Prune the cache based off of the LLDB settings each time we create a27// cache object.28ModuleListProperties &properties =29ModuleList::GetGlobalModuleListProperties();30// Only scan once an hour. If we have lots of debug sessions we don't want31// to scan this directory too often. A timestamp file is written to the32// directory to ensure different processes don't scan the directory too33// often. This setting doesn't mean that a thread will continually scan the34// cache directory within this process.35policy.Interval = std::chrono::hours(1);36// Get the user settings for pruning.37policy.MaxSizeBytes = properties.GetLLDBIndexCacheMaxByteSize();38policy.MaxSizePercentageOfAvailableSpace =39properties.GetLLDBIndexCacheMaxPercent();40policy.Expiration =41std::chrono::hours(properties.GetLLDBIndexCacheExpirationDays() * 24);42});43return policy;44}4546DataFileCache::DataFileCache(llvm::StringRef path, llvm::CachePruningPolicy policy) {47m_cache_dir.SetPath(path);48pruneCache(path, policy);4950// This lambda will get called when the data is gotten from the cache and51// also after the data was set for a given key. We only need to take52// ownership of the data if we are geting the data, so we use the53// m_take_ownership member variable to indicate if we need to take54// ownership.5556auto add_buffer = [this](unsigned task, const llvm::Twine &moduleName,57std::unique_ptr<llvm::MemoryBuffer> m) {58if (m_take_ownership)59m_mem_buff_up = std::move(m);60};61llvm::Expected<llvm::FileCache> cache_or_err =62llvm::localCache("LLDBModuleCache", "lldb-module", path, add_buffer);63if (cache_or_err)64m_cache_callback = std::move(*cache_or_err);65else {66Log *log = GetLog(LLDBLog::Modules);67LLDB_LOG_ERROR(log, cache_or_err.takeError(),68"failed to create lldb index cache directory: {0}");69}70}7172std::unique_ptr<llvm::MemoryBuffer>73DataFileCache::GetCachedData(llvm::StringRef key) {74std::lock_guard<std::mutex> guard(m_mutex);7576const unsigned task = 1;77m_take_ownership = true;78// If we call the "m_cache_callback" function and the data is cached, it will79// call the "add_buffer" lambda function from the constructor which will in80// turn take ownership of the member buffer that is passed to the callback and81// put it into a member variable.82llvm::Expected<llvm::AddStreamFn> add_stream_or_err =83m_cache_callback(task, key, "");84m_take_ownership = false;85// At this point we either already called the "add_buffer" lambda with86// the data or we haven't. We can tell if we got the cached data by checking87// the add_stream function pointer value below.88if (add_stream_or_err) {89llvm::AddStreamFn &add_stream = *add_stream_or_err;90// If the "add_stream" is nullptr, then the data was cached and we already91// called the "add_buffer" lambda. If it is valid, then if we were to call92// the add_stream function it would cause a cache file to get generated93// and we would be expected to fill in the data. In this function we only94// want to check if the data was cached, so we don't want to call95// "add_stream" in this function.96if (!add_stream)97return std::move(m_mem_buff_up);98} else {99Log *log = GetLog(LLDBLog::Modules);100LLDB_LOG_ERROR(log, add_stream_or_err.takeError(),101"failed to get the cache add stream callback for key: {0}");102}103// Data was not cached.104return std::unique_ptr<llvm::MemoryBuffer>();105}106107bool DataFileCache::SetCachedData(llvm::StringRef key,108llvm::ArrayRef<uint8_t> data) {109std::lock_guard<std::mutex> guard(m_mutex);110const unsigned task = 2;111// If we call this function and the data is cached, it will call the112// add_buffer lambda function from the constructor which will ignore the113// data.114llvm::Expected<llvm::AddStreamFn> add_stream_or_err =115m_cache_callback(task, key, "");116// If we reach this code then we either already called the callback with117// the data or we haven't. We can tell if we had the cached data by checking118// the CacheAddStream function pointer value below.119if (add_stream_or_err) {120llvm::AddStreamFn &add_stream = *add_stream_or_err;121// If the "add_stream" is nullptr, then the data was cached. If it is122// valid, then if we call the add_stream function with a task it will123// cause the file to get generated, but we only want to check if the data124// is cached here, so we don't want to call it here. Note that the125// add_buffer will also get called in this case after the data has been126// provided, but we won't take ownership of the memory buffer as we just127// want to write the data.128if (add_stream) {129llvm::Expected<std::unique_ptr<llvm::CachedFileStream>> file_or_err =130add_stream(task, "");131if (file_or_err) {132llvm::CachedFileStream *cfs = file_or_err->get();133cfs->OS->write((const char *)data.data(), data.size());134return true;135} else {136Log *log = GetLog(LLDBLog::Modules);137LLDB_LOG_ERROR(log, file_or_err.takeError(),138"failed to get the cache file stream for key: {0}");139}140}141} else {142Log *log = GetLog(LLDBLog::Modules);143LLDB_LOG_ERROR(log, add_stream_or_err.takeError(),144"failed to get the cache add stream callback for key: {0}");145}146return false;147}148149FileSpec DataFileCache::GetCacheFilePath(llvm::StringRef key) {150FileSpec cache_file(m_cache_dir);151std::string filename("llvmcache-");152filename += key.str();153cache_file.AppendPathComponent(filename);154return cache_file;155}156157Status DataFileCache::RemoveCacheFile(llvm::StringRef key) {158FileSpec cache_file = GetCacheFilePath(key);159FileSystem &fs = FileSystem::Instance();160if (!fs.Exists(cache_file))161return Status();162return fs.RemoveFile(cache_file);163}164165CacheSignature::CacheSignature(lldb_private::Module *module) {166Clear();167UUID uuid = module->GetUUID();168if (uuid.IsValid())169m_uuid = uuid;170171std::time_t mod_time = 0;172mod_time = llvm::sys::toTimeT(module->GetModificationTime());173if (mod_time != 0)174m_mod_time = mod_time;175176mod_time = llvm::sys::toTimeT(module->GetObjectModificationTime());177if (mod_time != 0)178m_obj_mod_time = mod_time;179}180181CacheSignature::CacheSignature(lldb_private::ObjectFile *objfile) {182Clear();183UUID uuid = objfile->GetUUID();184if (uuid.IsValid())185m_uuid = uuid;186187std::time_t mod_time = 0;188// Grab the modification time of the object file's file. It isn't always the189// same as the module's file when you have a executable file as the main190// executable, and you have a object file for a symbol file.191FileSystem &fs = FileSystem::Instance();192mod_time = llvm::sys::toTimeT(fs.GetModificationTime(objfile->GetFileSpec()));193if (mod_time != 0)194m_mod_time = mod_time;195196mod_time =197llvm::sys::toTimeT(objfile->GetModule()->GetObjectModificationTime());198if (mod_time != 0)199m_obj_mod_time = mod_time;200}201202enum SignatureEncoding {203eSignatureUUID = 1u,204eSignatureModTime = 2u,205eSignatureObjectModTime = 3u,206eSignatureEnd = 255u,207};208209bool CacheSignature::Encode(DataEncoder &encoder) const {210if (!IsValid())211return false; // Invalid signature, return false!212213if (m_uuid) {214llvm::ArrayRef<uint8_t> uuid_bytes = m_uuid->GetBytes();215encoder.AppendU8(eSignatureUUID);216encoder.AppendU8(uuid_bytes.size());217encoder.AppendData(uuid_bytes);218}219if (m_mod_time) {220encoder.AppendU8(eSignatureModTime);221encoder.AppendU32(*m_mod_time);222}223if (m_obj_mod_time) {224encoder.AppendU8(eSignatureObjectModTime);225encoder.AppendU32(*m_obj_mod_time);226}227encoder.AppendU8(eSignatureEnd);228return true;229}230231bool CacheSignature::Decode(const lldb_private::DataExtractor &data,232lldb::offset_t *offset_ptr) {233Clear();234while (uint8_t sig_encoding = data.GetU8(offset_ptr)) {235switch (sig_encoding) {236case eSignatureUUID: {237const uint8_t length = data.GetU8(offset_ptr);238const uint8_t *bytes = (const uint8_t *)data.GetData(offset_ptr, length);239if (bytes != nullptr && length > 0)240m_uuid = UUID(llvm::ArrayRef<uint8_t>(bytes, length));241} break;242case eSignatureModTime: {243uint32_t mod_time = data.GetU32(offset_ptr);244if (mod_time > 0)245m_mod_time = mod_time;246} break;247case eSignatureObjectModTime: {248uint32_t mod_time = data.GetU32(offset_ptr);249if (mod_time > 0)250m_obj_mod_time = mod_time;251} break;252case eSignatureEnd:253// The definition of is valid changed to only be valid if the UUID is254// valid so make sure that if we attempt to decode an old cache file255// that we will fail to decode the cache file if the signature isn't256// considered valid.257return IsValid();258default:259break;260}261}262return false;263}264265uint32_t ConstStringTable::Add(ConstString s) {266auto pos = m_string_to_offset.find(s);267if (pos != m_string_to_offset.end())268return pos->second;269const uint32_t offset = m_next_offset;270m_strings.push_back(s);271m_string_to_offset[s] = offset;272m_next_offset += s.GetLength() + 1;273return offset;274}275276static const llvm::StringRef kStringTableIdentifier("STAB");277278bool ConstStringTable::Encode(DataEncoder &encoder) {279// Write an 4 character code into the stream. This will help us when decoding280// to make sure we find this identifier when decoding the string table to make281// sure we have the rigth data. It also helps to identify the string table282// when dumping the hex bytes in a cache file.283encoder.AppendData(kStringTableIdentifier);284size_t length_offset = encoder.GetByteSize();285encoder.AppendU32(0); // Total length of all strings which will be fixed up.286size_t strtab_offset = encoder.GetByteSize();287encoder.AppendU8(0); // Start the string table with an empty string.288for (auto s: m_strings) {289// Make sure all of the offsets match up with what we handed out!290assert(m_string_to_offset.find(s)->second ==291encoder.GetByteSize() - strtab_offset);292// Append the C string into the encoder293encoder.AppendCString(s.GetStringRef());294}295// Fixup the string table length.296encoder.PutU32(length_offset, encoder.GetByteSize() - strtab_offset);297return true;298}299300bool StringTableReader::Decode(const lldb_private::DataExtractor &data,301lldb::offset_t *offset_ptr) {302llvm::StringRef identifier((const char *)data.GetData(offset_ptr, 4), 4);303if (identifier != kStringTableIdentifier)304return false;305const uint32_t length = data.GetU32(offset_ptr);306// We always have at least one byte for the empty string at offset zero.307if (length == 0)308return false;309const char *bytes = (const char *)data.GetData(offset_ptr, length);310if (bytes == nullptr)311return false;312m_data = llvm::StringRef(bytes, length);313return true;314}315316llvm::StringRef StringTableReader::Get(uint32_t offset) const {317if (offset >= m_data.size())318return llvm::StringRef();319return llvm::StringRef(m_data.data() + offset);320}321322323324