1 //===-- ModuleSpec.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_MODULESPEC_H
10 #define LLDB_CORE_MODULESPEC_H
11 
12 #include "lldb/Host/FileSystem.h"
13 #include "lldb/Target/PathMappingList.h"
14 #include "lldb/Utility/ArchSpec.h"
15 #include "lldb/Utility/FileSpec.h"
16 #include "lldb/Utility/Iterable.h"
17 #include "lldb/Utility/Stream.h"
18 #include "lldb/Utility/UUID.h"
19 
20 #include "llvm/Support/Chrono.h"
21 
22 #include <mutex>
23 #include <vector>
24 
25 namespace lldb_private {
26 
27 class ModuleSpec {
28 public:
29   ModuleSpec() = default;
30 
31   /// If the \c data argument is passed, its contents will be used
32   /// as the module contents instead of trying to read them from
33   /// \c file_spec .
34   ModuleSpec(const FileSpec &file_spec, const UUID &uuid = UUID(),
35              lldb::DataBufferSP data = lldb::DataBufferSP())
36       : m_file(file_spec), m_uuid(uuid), m_object_offset(0), m_data(data) {
37     if (data)
38       m_object_size = data->GetByteSize();
39     else if (m_file)
40       m_object_size = FileSystem::Instance().GetByteSize(file_spec);
41   }
42 
43   ModuleSpec(const FileSpec &file_spec, const ArchSpec &arch)
44       : m_file(file_spec), m_arch(arch), m_object_offset(0),
45         m_object_size(FileSystem::Instance().GetByteSize(file_spec)) {}
46 
47   FileSpec *GetFileSpecPtr() { return (m_file ? &m_file : nullptr); }
48 
49   const FileSpec *GetFileSpecPtr() const {
50     return (m_file ? &m_file : nullptr);
51   }
52 
53   FileSpec &GetFileSpec() { return m_file; }
54 
55   const FileSpec &GetFileSpec() const { return m_file; }
56 
57   FileSpec *GetPlatformFileSpecPtr() {
58     return (m_platform_file ? &m_platform_file : nullptr);
59   }
60 
61   const FileSpec *GetPlatformFileSpecPtr() const {
62     return (m_platform_file ? &m_platform_file : nullptr);
63   }
64 
65   FileSpec &GetPlatformFileSpec() { return m_platform_file; }
66 
67   const FileSpec &GetPlatformFileSpec() const { return m_platform_file; }
68 
69   FileSpec *GetSymbolFileSpecPtr() {
70     return (m_symbol_file ? &m_symbol_file : nullptr);
71   }
72 
73   const FileSpec *GetSymbolFileSpecPtr() const {
74     return (m_symbol_file ? &m_symbol_file : nullptr);
75   }
76 
77   FileSpec &GetSymbolFileSpec() { return m_symbol_file; }
78 
79   const FileSpec &GetSymbolFileSpec() const { return m_symbol_file; }
80 
81   ArchSpec *GetArchitecturePtr() {
82     return (m_arch.IsValid() ? &m_arch : nullptr);
83   }
84 
85   const ArchSpec *GetArchitecturePtr() const {
86     return (m_arch.IsValid() ? &m_arch : nullptr);
87   }
88 
89   ArchSpec &GetArchitecture() { return m_arch; }
90 
91   const ArchSpec &GetArchitecture() const { return m_arch; }
92 
93   UUID *GetUUIDPtr() { return (m_uuid.IsValid() ? &m_uuid : nullptr); }
94 
95   const UUID *GetUUIDPtr() const {
96     return (m_uuid.IsValid() ? &m_uuid : nullptr);
97   }
98 
99   UUID &GetUUID() { return m_uuid; }
100 
101   const UUID &GetUUID() const { return m_uuid; }
102 
103   ConstString &GetObjectName() { return m_object_name; }
104 
105   ConstString GetObjectName() const { return m_object_name; }
106 
107   uint64_t GetObjectOffset() const { return m_object_offset; }
108 
109   void SetObjectOffset(uint64_t object_offset) {
110     m_object_offset = object_offset;
111   }
112 
113   uint64_t GetObjectSize() const { return m_object_size; }
114 
115   void SetObjectSize(uint64_t object_size) { m_object_size = object_size; }
116 
117   llvm::sys::TimePoint<> &GetObjectModificationTime() {
118     return m_object_mod_time;
119   }
120 
121   const llvm::sys::TimePoint<> &GetObjectModificationTime() const {
122     return m_object_mod_time;
123   }
124 
125   PathMappingList &GetSourceMappingList() const { return m_source_mappings; }
126 
127   lldb::DataBufferSP GetData() const { return m_data; }
128 
129   void Clear() {
130     m_file.Clear();
131     m_platform_file.Clear();
132     m_symbol_file.Clear();
133     m_arch.Clear();
134     m_uuid.Clear();
135     m_object_name.Clear();
136     m_object_offset = 0;
137     m_object_size = 0;
138     m_source_mappings.Clear(false);
139     m_object_mod_time = llvm::sys::TimePoint<>();
140   }
141 
142   explicit operator bool() const {
143     if (m_file)
144       return true;
145     if (m_platform_file)
146       return true;
147     if (m_symbol_file)
148       return true;
149     if (m_arch.IsValid())
150       return true;
151     if (m_uuid.IsValid())
152       return true;
153     if (m_object_name)
154       return true;
155     if (m_object_size)
156       return true;
157     if (m_object_mod_time != llvm::sys::TimePoint<>())
158       return true;
159     return false;
160   }
161 
162   void Dump(Stream &strm) const {
163     bool dumped_something = false;
164     if (m_file) {
165       strm.PutCString("file = '");
166       strm << m_file;
167       strm.PutCString("'");
168       dumped_something = true;
169     }
170     if (m_platform_file) {
171       if (dumped_something)
172         strm.PutCString(", ");
173       strm.PutCString("platform_file = '");
174       strm << m_platform_file;
175       strm.PutCString("'");
176       dumped_something = true;
177     }
178     if (m_symbol_file) {
179       if (dumped_something)
180         strm.PutCString(", ");
181       strm.PutCString("symbol_file = '");
182       strm << m_symbol_file;
183       strm.PutCString("'");
184       dumped_something = true;
185     }
186     if (m_arch.IsValid()) {
187       if (dumped_something)
188         strm.PutCString(", ");
189       strm.Printf("arch = ");
190       m_arch.DumpTriple(strm.AsRawOstream());
191       dumped_something = true;
192     }
193     if (m_uuid.IsValid()) {
194       if (dumped_something)
195         strm.PutCString(", ");
196       strm.PutCString("uuid = ");
197       m_uuid.Dump(strm);
198       dumped_something = true;
199     }
200     if (m_object_name) {
201       if (dumped_something)
202         strm.PutCString(", ");
203       strm.Printf("object_name = %s", m_object_name.GetCString());
204       dumped_something = true;
205     }
206     if (m_object_offset > 0) {
207       if (dumped_something)
208         strm.PutCString(", ");
209       strm.Printf("object_offset = %" PRIu64, m_object_offset);
210       dumped_something = true;
211     }
212     if (m_object_size > 0) {
213       if (dumped_something)
214         strm.PutCString(", ");
215       strm.Printf("object size = %" PRIu64, m_object_size);
216       dumped_something = true;
217     }
218     if (m_object_mod_time != llvm::sys::TimePoint<>()) {
219       if (dumped_something)
220         strm.PutCString(", ");
221       strm.Format("object_mod_time = {0:x+}",
222                   uint64_t(llvm::sys::toTimeT(m_object_mod_time)));
223     }
224   }
225 
226   bool Matches(const ModuleSpec &match_module_spec,
227                bool exact_arch_match) const {
228     if (match_module_spec.GetUUIDPtr() &&
229         match_module_spec.GetUUID() != GetUUID())
230       return false;
231     if (match_module_spec.GetObjectName() &&
232         match_module_spec.GetObjectName() != GetObjectName())
233       return false;
234     if (!FileSpec::Match(match_module_spec.GetFileSpec(), GetFileSpec()))
235       return false;
236     if (GetPlatformFileSpec() &&
237         !FileSpec::Match(match_module_spec.GetPlatformFileSpec(),
238                          GetPlatformFileSpec())) {
239       return false;
240     }
241     // Only match the symbol file spec if there is one in this ModuleSpec
242     if (GetSymbolFileSpec() &&
243         !FileSpec::Match(match_module_spec.GetSymbolFileSpec(),
244                          GetSymbolFileSpec())) {
245       return false;
246     }
247     if (match_module_spec.GetArchitecturePtr()) {
248       if (exact_arch_match) {
249         if (!GetArchitecture().IsExactMatch(
250                 match_module_spec.GetArchitecture()))
251           return false;
252       } else {
253         if (!GetArchitecture().IsCompatibleMatch(
254                 match_module_spec.GetArchitecture()))
255           return false;
256       }
257     }
258     return true;
259   }
260 
261 protected:
262   FileSpec m_file;
263   FileSpec m_platform_file;
264   FileSpec m_symbol_file;
265   ArchSpec m_arch;
266   UUID m_uuid;
267   ConstString m_object_name;
268   uint64_t m_object_offset = 0;
269   uint64_t m_object_size = 0;
270   llvm::sys::TimePoint<> m_object_mod_time;
271   mutable PathMappingList m_source_mappings;
272   lldb::DataBufferSP m_data = {};
273 };
274 
275 class ModuleSpecList {
276 public:
277   ModuleSpecList() = default;
278 
279   ModuleSpecList(const ModuleSpecList &rhs) {
280     std::lock_guard<std::recursive_mutex> lhs_guard(m_mutex);
281     std::lock_guard<std::recursive_mutex> rhs_guard(rhs.m_mutex);
282     m_specs = rhs.m_specs;
283   }
284 
285   ~ModuleSpecList() = default;
286 
287   ModuleSpecList &operator=(const ModuleSpecList &rhs) {
288     if (this != &rhs) {
289       std::lock(m_mutex, rhs.m_mutex);
290       std::lock_guard<std::recursive_mutex> lhs_guard(m_mutex, std::adopt_lock);
291       std::lock_guard<std::recursive_mutex> rhs_guard(rhs.m_mutex,
292                                                       std::adopt_lock);
293       m_specs = rhs.m_specs;
294     }
295     return *this;
296   }
297 
298   size_t GetSize() const {
299     std::lock_guard<std::recursive_mutex> guard(m_mutex);
300     return m_specs.size();
301   }
302 
303   void Clear() {
304     std::lock_guard<std::recursive_mutex> guard(m_mutex);
305     m_specs.clear();
306   }
307 
308   void Append(const ModuleSpec &spec) {
309     std::lock_guard<std::recursive_mutex> guard(m_mutex);
310     m_specs.push_back(spec);
311   }
312 
313   void Append(const ModuleSpecList &rhs) {
314     std::lock_guard<std::recursive_mutex> lhs_guard(m_mutex);
315     std::lock_guard<std::recursive_mutex> rhs_guard(rhs.m_mutex);
316     m_specs.insert(m_specs.end(), rhs.m_specs.begin(), rhs.m_specs.end());
317   }
318 
319   // The index "i" must be valid and this can't be used in multi-threaded code
320   // as no mutex lock is taken.
321   ModuleSpec &GetModuleSpecRefAtIndex(size_t i) { return m_specs[i]; }
322 
323   bool GetModuleSpecAtIndex(size_t i, ModuleSpec &module_spec) const {
324     std::lock_guard<std::recursive_mutex> guard(m_mutex);
325     if (i < m_specs.size()) {
326       module_spec = m_specs[i];
327       return true;
328     }
329     module_spec.Clear();
330     return false;
331   }
332 
333   bool FindMatchingModuleSpec(const ModuleSpec &module_spec,
334                               ModuleSpec &match_module_spec) const {
335     std::lock_guard<std::recursive_mutex> guard(m_mutex);
336     bool exact_arch_match = true;
337     for (auto spec : m_specs) {
338       if (spec.Matches(module_spec, exact_arch_match)) {
339         match_module_spec = spec;
340         return true;
341       }
342     }
343 
344     // If there was an architecture, retry with a compatible arch
345     if (module_spec.GetArchitecturePtr()) {
346       exact_arch_match = false;
347       for (auto spec : m_specs) {
348         if (spec.Matches(module_spec, exact_arch_match)) {
349           match_module_spec = spec;
350           return true;
351         }
352       }
353     }
354     match_module_spec.Clear();
355     return false;
356   }
357 
358   void FindMatchingModuleSpecs(const ModuleSpec &module_spec,
359                                ModuleSpecList &matching_list) const {
360     std::lock_guard<std::recursive_mutex> guard(m_mutex);
361     bool exact_arch_match = true;
362     const size_t initial_match_count = matching_list.GetSize();
363     for (auto spec : m_specs) {
364       if (spec.Matches(module_spec, exact_arch_match))
365         matching_list.Append(spec);
366     }
367 
368     // If there was an architecture, retry with a compatible arch if no matches
369     // were found
370     if (module_spec.GetArchitecturePtr() &&
371         (initial_match_count == matching_list.GetSize())) {
372       exact_arch_match = false;
373       for (auto spec : m_specs) {
374         if (spec.Matches(module_spec, exact_arch_match))
375           matching_list.Append(spec);
376       }
377     }
378   }
379 
380   void Dump(Stream &strm) {
381     std::lock_guard<std::recursive_mutex> guard(m_mutex);
382     uint32_t idx = 0;
383     for (auto spec : m_specs) {
384       strm.Printf("[%u] ", idx);
385       spec.Dump(strm);
386       strm.EOL();
387       ++idx;
388     }
389   }
390 
391   typedef std::vector<ModuleSpec> collection;
392   typedef LockingAdaptedIterable<collection, ModuleSpec, vector_adapter,
393                                  std::recursive_mutex>
394       ModuleSpecIterable;
395 
396   ModuleSpecIterable ModuleSpecs() {
397     return ModuleSpecIterable(m_specs, m_mutex);
398   }
399 
400 protected:
401   collection m_specs;                         ///< The collection of modules.
402   mutable std::recursive_mutex m_mutex;
403 };
404 
405 } // namespace lldb_private
406 
407 #endif // LLDB_CORE_MODULESPEC_H
408