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