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
24namespace lldb_private {
25
26class ModuleSpec {
27public:
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 \c data argument is passed, its contents will be used
34 /// as the module contents instead of trying to read them from
35 /// \c 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
267protected:
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
281class ModuleSpecList {
282public:
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
397protected:
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