1 //===-- DataFileCache.h -----------------------------------------*- C++ -*-===//
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 #ifndef LLDB_CORE_DATAFILECACHE_H
10 #define LLDB_CORE_DATAFILECACHE_H
11 
12 #include "lldb/Utility/FileSpec.h"
13 #include "lldb/Utility/Status.h"
14 #include "lldb/Utility/UUID.h"
15 #include "lldb/lldb-forward.h"
16 #include "llvm/ADT/DenseMap.h"
17 #include "llvm/Support/Caching.h"
18 #include <mutex>
19 
20 namespace lldb_private {
21 
22 /// This class enables data to be cached into a directory using the llvm
23 /// caching code. Data can be stored and accessed using a unique string key.
24 /// The data will be stored in the directory that is specified in the
25 /// DataFileCache constructor. The data will be stored in files that start with
26 /// "llvmcache-<key>" where <key> is the key name specified when getting to
27 /// setting cached data.
28 ///
29 /// Sample code for how to use the cache:
30 ///
31 ///   DataFileCache cache("/tmp/lldb-test-cache");
32 ///   StringRef key("Key1");
33 ///   auto mem_buffer_up = cache.GetCachedData(key);
34 ///   if (mem_buffer_up) {
35 ///     printf("cached data:\n%s", mem_buffer_up->getBufferStart());
36 ///   } else {
37 ///     std::vector<uint8_t> data = { 'h', 'e', 'l', 'l', 'o', '\n' };
38 ///     cache.SetCachedData(key, data);
39 ///   }
40 
41 class DataFileCache {
42 public:
43   /// Create a data file cache in the directory path that is specified.
44   ///
45   /// Data will be cached in files created in this directory when clients call
46   /// DataFileCache::SetCacheData.
47   DataFileCache(llvm::StringRef path);
48 
49   /// Get cached data from the cache directory for the specified key.
50   ///
51   /// Keys must be unique for any given data. This function attempts to see if
52   /// the data is available for the specified key and will return a valid memory
53   /// buffer is data is available.
54   ///
55   /// \param key
56   ///   The unique string key that identifies data being cached.
57   ///
58   /// \return
59   ///   A valid unique pointer to a memory buffer if the data is available, or
60   ///   a unique pointer that contains NULL if the data is not available.
61   std::unique_ptr<llvm::MemoryBuffer> GetCachedData(llvm::StringRef key);
62 
63   /// Set cached data for the specified key.
64   ///
65   /// Setting the cached data will save a file in the cache directory to contain
66   /// the specified data.
67   ///
68   /// \param key
69   ///   The unique string key that identifies data being cached.
70   ///
71   /// \return
72   ///   True if the data was successfully cached, false otherwise.
73   bool SetCachedData(llvm::StringRef key, llvm::ArrayRef<uint8_t> data);
74 
75   /// Remove the cache file associated with the key.
76   Status RemoveCacheFile(llvm::StringRef key);
77 
78 private:
79   /// Return the cache file that is associated with the key.
80   FileSpec GetCacheFilePath(llvm::StringRef key);
81 
82   llvm::FileCache m_cache_callback;
83   FileSpec m_cache_dir;
84   std::mutex m_mutex;
85   std::unique_ptr<llvm::MemoryBuffer> m_mem_buff_up;
86   bool m_take_ownership = false;
87 };
88 
89 /// A signature for a given file on disk.
90 ///
91 /// Any files that are cached in the LLDB index cached need some data that
92 /// uniquely identifies a file on disk and this information should be written
93 /// into each cache file so we can validate if the cache file still matches
94 /// the file we are trying to load cached data for. Objects can fill out this
95 /// signature and then encode and decode them to validate the signatures
96 /// match. If they do not match, the cache file on disk should be removed as
97 /// it is out of date.
98 struct CacheSignature {
99   /// UUID of object file or module.
100   llvm::Optional<UUID> m_uuid = llvm::None;
101   /// Modification time of file on disk.
102   llvm::Optional<std::time_t> m_mod_time = llvm::None;
103   /// If this describes a .o file with a BSD archive, the BSD archive's
104   /// modification time will be in m_mod_time, and the .o file's modification
105   /// time will be in this m_obj_mod_time.
106   llvm::Optional<std::time_t> m_obj_mod_time = llvm::None;
107 
108   CacheSignature() = default;
109 
110   /// Create a signature from a module.
111   CacheSignature(lldb_private::Module *module);
112 
113   /// Create a signature from an object file.
114   CacheSignature(lldb_private::ObjectFile *objfile);
115 
116   void Clear() {
117     m_uuid = llvm::None;
118     m_mod_time = llvm::None;
119     m_obj_mod_time = llvm::None;
120   }
121 
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.has_value(); }
129 
130   /// Check if two signatures are the same.
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;
134   }
135 
136   /// Check if two signatures differ.
137   bool operator!=(const CacheSignature &rhs) const { return !(*this == rhs); }
138   /// Encode this object into a data encoder object.
139   ///
140   /// This allows this object to be serialized to disk. The CacheSignature
141   /// object must have at least one member variable that has a value in order to
142   /// be serialized so that we can match this data to when the cached file is
143   /// loaded at a later time.
144   ///
145   /// \param encoder
146   ///   A data encoder object that serialized bytes will be encoded into.
147   ///
148   /// \return
149   ///   True if a signature was encoded, and false if there were no member
150   ///   variables that had value. False indicates this data should not be
151   ///   cached to disk because we were unable to encode a valid signature.
152   bool Encode(DataEncoder &encoder) const;
153 
154   /// Decode a serialized version of this object from data.
155   ///
156   /// \param data
157   ///   The decoder object that references the serialized data.
158   ///
159   /// \param offset_ptr
160   ///   A pointer that contains the offset from which the data will be decoded
161   ///   from that gets updated as data gets decoded.
162   ///
163   /// \return
164   ///   True if the signature was successfully decoded, false otherwise.
165   bool Decode(const DataExtractor &data, lldb::offset_t *offset_ptr);
166 };
167 
168 /// Many cache files require string tables to store data efficiently. This
169 /// class helps create string tables.
170 class ConstStringTable {
171 public:
172   ConstStringTable() = default;
173   /// Add a string into the string table.
174   ///
175   /// Add a string to the string table will only add the same string one time
176   /// and will return the offset in the string table buffer to that string.
177   /// String tables are easy to build with ConstString objects since most LLDB
178   /// classes for symbol or debug info use them already and they provide
179   /// permanent storage for the string.
180   ///
181   /// \param s
182   ///   The string to insert into the string table.
183   ///
184   /// \return
185   ///   The byte offset from the start of the string table for the inserted
186   ///   string. Duplicate strings that get inserted will return the same
187   ///   byte offset.
188   uint32_t Add(ConstString s);
189 
190   bool Encode(DataEncoder &encoder);
191 
192 private:
193   std::vector<ConstString> m_strings;
194   llvm::DenseMap<ConstString, uint32_t> m_string_to_offset;
195   /// Skip one byte to start the string table off with an empty string.
196   uint32_t m_next_offset = 1;
197 };
198 
199 /// Many cache files require string tables to store data efficiently. This
200 /// class helps give out strings from a string table that was read from a
201 /// cache file.
202 class StringTableReader {
203 public:
204   StringTableReader() = default;
205 
206   llvm::StringRef Get(uint32_t offset) const;
207 
208   bool Decode(const DataExtractor &data, lldb::offset_t *offset_ptr);
209 
210 protected:
211   /// All of the strings in the string table are contained in m_data.
212   llvm::StringRef m_data;
213 };
214 
215 } // namespace lldb_private
216 
217 #endif // LLDB_CORE_DATAFILECACHE_H
218