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