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
25namespace lldb_private {
26
27class ModuleSpec {
28public:
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
261protected:
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
275class ModuleSpecList {
276public:
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
400protected:
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