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