1 | //===-- SearchFilter.cpp --------------------------------------------------===// |
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 | #include "lldb/Core/SearchFilter.h" |
10 | |
11 | #include "lldb/Breakpoint/Breakpoint.h" |
12 | #include "lldb/Core/Module.h" |
13 | #include "lldb/Core/ModuleList.h" |
14 | #include "lldb/Symbol/CompileUnit.h" |
15 | #include "lldb/Symbol/SymbolContext.h" |
16 | #include "lldb/Symbol/SymbolFile.h" |
17 | #include "lldb/Target/Target.h" |
18 | #include "lldb/Utility/ConstString.h" |
19 | #include "lldb/Utility/Status.h" |
20 | #include "lldb/Utility/Stream.h" |
21 | #include "lldb/lldb-enumerations.h" |
22 | |
23 | #include "llvm/ADT/StringRef.h" |
24 | #include "llvm/Support/ErrorHandling.h" |
25 | |
26 | #include <memory> |
27 | #include <mutex> |
28 | #include <string> |
29 | |
30 | #include <cinttypes> |
31 | #include <cstring> |
32 | |
33 | namespace lldb_private { |
34 | class Address; |
35 | } |
36 | namespace lldb_private { |
37 | class Function; |
38 | } |
39 | |
40 | using namespace lldb; |
41 | using namespace lldb_private; |
42 | |
43 | const char *SearchFilter::g_ty_to_name[] = {"Unconstrained" , "Exception" , |
44 | "Module" , "Modules" , |
45 | "ModulesAndCU" , "Unknown" }; |
46 | |
47 | const char |
48 | *SearchFilter::g_option_names[SearchFilter::OptionNames::LastOptionName] = { |
49 | "ModuleList" , "CUList" }; |
50 | |
51 | const char *SearchFilter::FilterTyToName(enum FilterTy type) { |
52 | if (type > LastKnownFilterType) |
53 | return g_ty_to_name[UnknownFilter]; |
54 | |
55 | return g_ty_to_name[type]; |
56 | } |
57 | |
58 | SearchFilter::FilterTy SearchFilter::NameToFilterTy(llvm::StringRef name) { |
59 | for (size_t i = 0; i <= LastKnownFilterType; i++) { |
60 | if (name == g_ty_to_name[i]) |
61 | return (FilterTy)i; |
62 | } |
63 | return UnknownFilter; |
64 | } |
65 | |
66 | Searcher::Searcher() = default; |
67 | |
68 | Searcher::~Searcher() = default; |
69 | |
70 | void Searcher::GetDescription(Stream *s) {} |
71 | |
72 | SearchFilter::SearchFilter(const TargetSP &target_sp, unsigned char filterType) |
73 | : m_target_sp(target_sp), SubclassID(filterType) {} |
74 | |
75 | SearchFilter::~SearchFilter() = default; |
76 | |
77 | SearchFilterSP SearchFilter::CreateFromStructuredData( |
78 | const lldb::TargetSP& target_sp, |
79 | const StructuredData::Dictionary &filter_dict, |
80 | Status &error) { |
81 | SearchFilterSP result_sp; |
82 | if (!filter_dict.IsValid()) { |
83 | error.SetErrorString("Can't deserialize from an invalid data object." ); |
84 | return result_sp; |
85 | } |
86 | |
87 | llvm::StringRef subclass_name; |
88 | |
89 | bool success = filter_dict.GetValueForKeyAsString( |
90 | key: GetSerializationSubclassKey(), result&: subclass_name); |
91 | if (!success) { |
92 | error.SetErrorString("Filter data missing subclass key" ); |
93 | return result_sp; |
94 | } |
95 | |
96 | FilterTy filter_type = NameToFilterTy(name: subclass_name); |
97 | if (filter_type == UnknownFilter) { |
98 | error.SetErrorStringWithFormatv(format: "Unknown filter type: {0}." , args&: subclass_name); |
99 | return result_sp; |
100 | } |
101 | |
102 | StructuredData::Dictionary *subclass_options = nullptr; |
103 | success = filter_dict.GetValueForKeyAsDictionary( |
104 | key: GetSerializationSubclassOptionsKey(), result&: subclass_options); |
105 | if (!success || !subclass_options || !subclass_options->IsValid()) { |
106 | error.SetErrorString("Filter data missing subclass options key." ); |
107 | return result_sp; |
108 | } |
109 | |
110 | switch (filter_type) { |
111 | case Unconstrained: |
112 | result_sp = SearchFilterForUnconstrainedSearches::CreateFromStructuredData( |
113 | target_sp, data_dict: *subclass_options, error); |
114 | break; |
115 | case ByModule: |
116 | result_sp = SearchFilterByModule::CreateFromStructuredData( |
117 | target_sp, data_dict: *subclass_options, error); |
118 | break; |
119 | case ByModules: |
120 | result_sp = SearchFilterByModuleList::CreateFromStructuredData( |
121 | target_sp, data_dict: *subclass_options, error); |
122 | break; |
123 | case ByModulesAndCU: |
124 | result_sp = SearchFilterByModuleListAndCU::CreateFromStructuredData( |
125 | target_sp, data_dict: *subclass_options, error); |
126 | break; |
127 | case Exception: |
128 | error.SetErrorString("Can't serialize exception breakpoints yet." ); |
129 | break; |
130 | default: |
131 | llvm_unreachable("Should never get an uresolvable filter type." ); |
132 | } |
133 | |
134 | return result_sp; |
135 | } |
136 | |
137 | bool SearchFilter::ModulePasses(const FileSpec &spec) { return true; } |
138 | |
139 | bool SearchFilter::ModulePasses(const ModuleSP &module_sp) { return true; } |
140 | |
141 | bool SearchFilter::AddressPasses(Address &address) { return true; } |
142 | |
143 | bool SearchFilter::CompUnitPasses(FileSpec &fileSpec) { return true; } |
144 | |
145 | bool SearchFilter::CompUnitPasses(CompileUnit &compUnit) { return true; } |
146 | |
147 | bool SearchFilter::FunctionPasses(Function &function) { |
148 | // This is a slightly cheesy job, but since we don't have finer grained |
149 | // filters yet, just checking that the start address passes is probably |
150 | // good enough for the base class behavior. |
151 | Address addr = function.GetAddressRange().GetBaseAddress(); |
152 | return AddressPasses(address&: addr); |
153 | } |
154 | |
155 | |
156 | uint32_t SearchFilter::GetFilterRequiredItems() { |
157 | return (lldb::SymbolContextItem)0; |
158 | } |
159 | |
160 | void SearchFilter::GetDescription(Stream *s) {} |
161 | |
162 | void SearchFilter::Dump(Stream *s) const {} |
163 | |
164 | lldb::SearchFilterSP SearchFilter::CreateCopy(lldb::TargetSP& target_sp) { |
165 | SearchFilterSP ret_sp = DoCreateCopy(); |
166 | ret_sp->SetTarget(target_sp); |
167 | return ret_sp; |
168 | } |
169 | |
170 | // Helper functions for serialization. |
171 | |
172 | StructuredData::DictionarySP |
173 | SearchFilter::WrapOptionsDict(StructuredData::DictionarySP options_dict_sp) { |
174 | if (!options_dict_sp || !options_dict_sp->IsValid()) |
175 | return StructuredData::DictionarySP(); |
176 | |
177 | auto type_dict_sp = std::make_shared<StructuredData::Dictionary>(); |
178 | type_dict_sp->AddStringItem(key: GetSerializationSubclassKey(), value: GetFilterName()); |
179 | type_dict_sp->AddItem(key: GetSerializationSubclassOptionsKey(), value_sp: options_dict_sp); |
180 | |
181 | return type_dict_sp; |
182 | } |
183 | |
184 | void SearchFilter::SerializeFileSpecList( |
185 | StructuredData::DictionarySP &options_dict_sp, OptionNames name, |
186 | FileSpecList &file_list) { |
187 | size_t num_modules = file_list.GetSize(); |
188 | |
189 | // Don't serialize empty lists. |
190 | if (num_modules == 0) |
191 | return; |
192 | |
193 | auto module_array_sp = std::make_shared<StructuredData::Array>(); |
194 | for (size_t i = 0; i < num_modules; i++) { |
195 | module_array_sp->AddItem(item: std::make_shared<StructuredData::String>( |
196 | args: file_list.GetFileSpecAtIndex(idx: i).GetPath())); |
197 | } |
198 | options_dict_sp->AddItem(key: GetKey(enum_value: name), value_sp: module_array_sp); |
199 | } |
200 | |
201 | // UTILITY Functions to help iterate down through the elements of the |
202 | // SymbolContext. |
203 | |
204 | void SearchFilter::Search(Searcher &searcher) { |
205 | SymbolContext empty_sc; |
206 | |
207 | if (!m_target_sp) |
208 | return; |
209 | empty_sc.target_sp = m_target_sp; |
210 | |
211 | if (searcher.GetDepth() == lldb::eSearchDepthTarget) { |
212 | searcher.SearchCallback(filter&: *this, context&: empty_sc, addr: nullptr); |
213 | return; |
214 | } |
215 | |
216 | DoModuleIteration(context: empty_sc, searcher); |
217 | } |
218 | |
219 | void SearchFilter::SearchInModuleList(Searcher &searcher, ModuleList &modules) { |
220 | SymbolContext empty_sc; |
221 | |
222 | if (!m_target_sp) |
223 | return; |
224 | empty_sc.target_sp = m_target_sp; |
225 | |
226 | if (searcher.GetDepth() == lldb::eSearchDepthTarget) { |
227 | searcher.SearchCallback(filter&: *this, context&: empty_sc, addr: nullptr); |
228 | return; |
229 | } |
230 | |
231 | for (ModuleSP module_sp : modules.Modules()) { |
232 | if (!ModulePasses(module_sp)) |
233 | continue; |
234 | if (DoModuleIteration(module_sp, searcher) == Searcher::eCallbackReturnStop) |
235 | return; |
236 | } |
237 | } |
238 | |
239 | Searcher::CallbackReturn |
240 | SearchFilter::DoModuleIteration(const lldb::ModuleSP &module_sp, |
241 | Searcher &searcher) { |
242 | SymbolContext matchingContext(m_target_sp, module_sp); |
243 | return DoModuleIteration(context: matchingContext, searcher); |
244 | } |
245 | |
246 | Searcher::CallbackReturn |
247 | SearchFilter::DoModuleIteration(const SymbolContext &context, |
248 | Searcher &searcher) { |
249 | if (searcher.GetDepth() < lldb::eSearchDepthModule) |
250 | return Searcher::eCallbackReturnContinue; |
251 | |
252 | if (context.module_sp) { |
253 | if (searcher.GetDepth() != lldb::eSearchDepthModule) |
254 | return DoCUIteration(module_sp: context.module_sp, context, searcher); |
255 | |
256 | SymbolContext matchingContext(context.module_sp.get()); |
257 | searcher.SearchCallback(filter&: *this, context&: matchingContext, addr: nullptr); |
258 | return Searcher::eCallbackReturnContinue; |
259 | } |
260 | |
261 | for (ModuleSP module_sp : m_target_sp->GetImages().Modules()) { |
262 | // If this is the last level supplied, then call the callback directly, |
263 | // otherwise descend. |
264 | if (!ModulePasses(module_sp)) |
265 | continue; |
266 | |
267 | if (searcher.GetDepth() == lldb::eSearchDepthModule) { |
268 | SymbolContext matchingContext(m_target_sp, module_sp); |
269 | |
270 | Searcher::CallbackReturn shouldContinue = |
271 | searcher.SearchCallback(filter&: *this, context&: matchingContext, addr: nullptr); |
272 | if (shouldContinue == Searcher::eCallbackReturnStop || |
273 | shouldContinue == Searcher::eCallbackReturnPop) |
274 | return shouldContinue; |
275 | } else { |
276 | Searcher::CallbackReturn shouldContinue = |
277 | DoCUIteration(module_sp, context, searcher); |
278 | if (shouldContinue == Searcher::eCallbackReturnStop) |
279 | return shouldContinue; |
280 | else if (shouldContinue == Searcher::eCallbackReturnPop) |
281 | continue; |
282 | } |
283 | } |
284 | |
285 | return Searcher::eCallbackReturnContinue; |
286 | } |
287 | |
288 | Searcher::CallbackReturn |
289 | SearchFilter::DoCUIteration(const ModuleSP &module_sp, |
290 | const SymbolContext &context, Searcher &searcher) { |
291 | Searcher::CallbackReturn shouldContinue; |
292 | if (context.comp_unit != nullptr) { |
293 | if (CompUnitPasses(compUnit&: *context.comp_unit)) { |
294 | SymbolContext matchingContext(m_target_sp, module_sp, context.comp_unit); |
295 | return searcher.SearchCallback(filter&: *this, context&: matchingContext, addr: nullptr); |
296 | } |
297 | return Searcher::eCallbackReturnContinue; |
298 | } |
299 | |
300 | const size_t num_comp_units = module_sp->GetNumCompileUnits(); |
301 | for (size_t i = 0; i < num_comp_units; i++) { |
302 | CompUnitSP cu_sp(module_sp->GetCompileUnitAtIndex(idx: i)); |
303 | if (!cu_sp) |
304 | continue; |
305 | if (!CompUnitPasses(compUnit&: *(cu_sp.get()))) |
306 | continue; |
307 | |
308 | if (searcher.GetDepth() == lldb::eSearchDepthCompUnit) { |
309 | SymbolContext matchingContext(m_target_sp, module_sp, cu_sp.get()); |
310 | |
311 | shouldContinue = searcher.SearchCallback(filter&: *this, context&: matchingContext, addr: nullptr); |
312 | |
313 | if (shouldContinue == Searcher::eCallbackReturnPop) |
314 | return Searcher::eCallbackReturnContinue; |
315 | else if (shouldContinue == Searcher::eCallbackReturnStop) |
316 | return shouldContinue; |
317 | continue; |
318 | } |
319 | |
320 | // First make sure this compile unit's functions are parsed |
321 | // since CompUnit::ForeachFunction only iterates over already |
322 | // parsed functions. |
323 | SymbolFile *sym_file = module_sp->GetSymbolFile(); |
324 | if (!sym_file) |
325 | continue; |
326 | if (!sym_file->ParseFunctions(comp_unit&: *cu_sp)) |
327 | continue; |
328 | // If we got any functions, use ForeachFunction to do the iteration. |
329 | cu_sp->ForeachFunction(lambda: [&](const FunctionSP &func_sp) { |
330 | if (!FunctionPasses(function&: *func_sp.get())) |
331 | return false; // Didn't pass the filter, just keep going. |
332 | if (searcher.GetDepth() == lldb::eSearchDepthFunction) { |
333 | SymbolContext matchingContext(m_target_sp, module_sp, cu_sp.get(), |
334 | func_sp.get()); |
335 | shouldContinue = |
336 | searcher.SearchCallback(filter&: *this, context&: matchingContext, addr: nullptr); |
337 | } else { |
338 | shouldContinue = DoFunctionIteration(function: func_sp.get(), context, searcher); |
339 | } |
340 | return shouldContinue != Searcher::eCallbackReturnContinue; |
341 | }); |
342 | } |
343 | return Searcher::eCallbackReturnContinue; |
344 | } |
345 | |
346 | Searcher::CallbackReturn SearchFilter::DoFunctionIteration( |
347 | Function *function, const SymbolContext &context, Searcher &searcher) { |
348 | // FIXME: Implement... |
349 | return Searcher::eCallbackReturnContinue; |
350 | } |
351 | |
352 | // SearchFilterForUnconstrainedSearches: |
353 | // Selects a shared library matching a given file spec, consulting the targets |
354 | // "black list". |
355 | SearchFilterSP SearchFilterForUnconstrainedSearches::CreateFromStructuredData( |
356 | const lldb::TargetSP& target_sp, |
357 | const StructuredData::Dictionary &data_dict, |
358 | Status &error) { |
359 | // No options for an unconstrained search. |
360 | return std::make_shared<SearchFilterForUnconstrainedSearches>(args: target_sp); |
361 | } |
362 | |
363 | StructuredData::ObjectSP |
364 | SearchFilterForUnconstrainedSearches::SerializeToStructuredData() { |
365 | // The options dictionary is an empty dictionary: |
366 | auto result_sp = std::make_shared<StructuredData::Dictionary>(); |
367 | return WrapOptionsDict(options_dict_sp: result_sp); |
368 | } |
369 | |
370 | bool SearchFilterForUnconstrainedSearches::ModulePasses( |
371 | const FileSpec &module_spec) { |
372 | return !m_target_sp->ModuleIsExcludedForUnconstrainedSearches(module_spec); |
373 | } |
374 | |
375 | bool SearchFilterForUnconstrainedSearches::ModulePasses( |
376 | const lldb::ModuleSP &module_sp) { |
377 | if (!module_sp) |
378 | return true; |
379 | else if (m_target_sp->ModuleIsExcludedForUnconstrainedSearches(module_sp)) |
380 | return false; |
381 | return true; |
382 | } |
383 | |
384 | SearchFilterSP SearchFilterForUnconstrainedSearches::DoCreateCopy() { |
385 | return std::make_shared<SearchFilterForUnconstrainedSearches>(args&: *this); |
386 | } |
387 | |
388 | // SearchFilterByModule: |
389 | // Selects a shared library matching a given file spec |
390 | |
391 | SearchFilterByModule::SearchFilterByModule(const lldb::TargetSP &target_sp, |
392 | const FileSpec &module) |
393 | : SearchFilter(target_sp, FilterTy::ByModule), m_module_spec(module) {} |
394 | |
395 | SearchFilterByModule::~SearchFilterByModule() = default; |
396 | |
397 | bool SearchFilterByModule::ModulePasses(const ModuleSP &module_sp) { |
398 | return (module_sp && |
399 | FileSpec::Match(pattern: m_module_spec, file: module_sp->GetFileSpec())); |
400 | } |
401 | |
402 | bool SearchFilterByModule::ModulePasses(const FileSpec &spec) { |
403 | return FileSpec::Match(pattern: m_module_spec, file: spec); |
404 | } |
405 | |
406 | bool SearchFilterByModule::AddressPasses(Address &address) { |
407 | // FIXME: Not yet implemented |
408 | return true; |
409 | } |
410 | |
411 | void SearchFilterByModule::Search(Searcher &searcher) { |
412 | if (!m_target_sp) |
413 | return; |
414 | |
415 | if (searcher.GetDepth() == lldb::eSearchDepthTarget) { |
416 | SymbolContext empty_sc; |
417 | empty_sc.target_sp = m_target_sp; |
418 | searcher.SearchCallback(filter&: *this, context&: empty_sc, addr: nullptr); |
419 | } |
420 | |
421 | // If the module file spec is a full path, then we can just find the one |
422 | // filespec that passes. Otherwise, we need to go through all modules and |
423 | // find the ones that match the file name. |
424 | |
425 | const ModuleList &target_modules = m_target_sp->GetImages(); |
426 | std::lock_guard<std::recursive_mutex> guard(target_modules.GetMutex()); |
427 | |
428 | for (ModuleSP module_sp : m_target_sp->GetImages().Modules()) { |
429 | if (FileSpec::Match(pattern: m_module_spec, file: module_sp->GetFileSpec())) { |
430 | SymbolContext matchingContext(m_target_sp, module_sp); |
431 | Searcher::CallbackReturn shouldContinue; |
432 | |
433 | shouldContinue = DoModuleIteration(context: matchingContext, searcher); |
434 | if (shouldContinue == Searcher::eCallbackReturnStop) |
435 | return; |
436 | } |
437 | } |
438 | } |
439 | |
440 | void SearchFilterByModule::GetDescription(Stream *s) { |
441 | s->PutCString(cstr: ", module = " ); |
442 | s->PutCString(cstr: m_module_spec.GetFilename().AsCString(value_if_empty: "<Unknown>" )); |
443 | } |
444 | |
445 | uint32_t SearchFilterByModule::GetFilterRequiredItems() { |
446 | return eSymbolContextModule; |
447 | } |
448 | |
449 | void SearchFilterByModule::Dump(Stream *s) const {} |
450 | |
451 | SearchFilterSP SearchFilterByModule::DoCreateCopy() { |
452 | return std::make_shared<SearchFilterByModule>(args&: *this); |
453 | } |
454 | |
455 | SearchFilterSP SearchFilterByModule::CreateFromStructuredData( |
456 | const lldb::TargetSP& target_sp, |
457 | const StructuredData::Dictionary &data_dict, |
458 | Status &error) { |
459 | StructuredData::Array *modules_array; |
460 | bool success = data_dict.GetValueForKeyAsArray(key: GetKey(enum_value: OptionNames::ModList), |
461 | result&: modules_array); |
462 | if (!success) { |
463 | error.SetErrorString("SFBM::CFSD: Could not find the module list key." ); |
464 | return nullptr; |
465 | } |
466 | |
467 | size_t num_modules = modules_array->GetSize(); |
468 | if (num_modules > 1) { |
469 | error.SetErrorString( |
470 | "SFBM::CFSD: Only one modules allowed for SearchFilterByModule." ); |
471 | return nullptr; |
472 | } |
473 | |
474 | std::optional<llvm::StringRef> maybe_module = |
475 | modules_array->GetItemAtIndexAsString(idx: 0); |
476 | if (!maybe_module) { |
477 | error.SetErrorString("SFBM::CFSD: filter module item not a string." ); |
478 | return nullptr; |
479 | } |
480 | FileSpec module_spec(*maybe_module); |
481 | |
482 | return std::make_shared<SearchFilterByModule>(args: target_sp, args&: module_spec); |
483 | } |
484 | |
485 | StructuredData::ObjectSP SearchFilterByModule::SerializeToStructuredData() { |
486 | auto options_dict_sp = std::make_shared<StructuredData::Dictionary>(); |
487 | auto module_array_sp = std::make_shared<StructuredData::Array>(); |
488 | module_array_sp->AddItem( |
489 | item: std::make_shared<StructuredData::String>(args: m_module_spec.GetPath())); |
490 | options_dict_sp->AddItem(key: GetKey(enum_value: OptionNames::ModList), value_sp: module_array_sp); |
491 | return WrapOptionsDict(options_dict_sp); |
492 | } |
493 | |
494 | // SearchFilterByModuleList: |
495 | // Selects a shared library matching a given file spec |
496 | |
497 | SearchFilterByModuleList::SearchFilterByModuleList( |
498 | const lldb::TargetSP &target_sp, const FileSpecList &module_list) |
499 | : SearchFilter(target_sp, FilterTy::ByModules), |
500 | m_module_spec_list(module_list) {} |
501 | |
502 | SearchFilterByModuleList::SearchFilterByModuleList( |
503 | const lldb::TargetSP &target_sp, const FileSpecList &module_list, |
504 | enum FilterTy filter_ty) |
505 | : SearchFilter(target_sp, filter_ty), m_module_spec_list(module_list) {} |
506 | |
507 | SearchFilterByModuleList::~SearchFilterByModuleList() = default; |
508 | |
509 | bool SearchFilterByModuleList::ModulePasses(const ModuleSP &module_sp) { |
510 | if (m_module_spec_list.GetSize() == 0) |
511 | return true; |
512 | |
513 | return module_sp && m_module_spec_list.FindFileIndex( |
514 | idx: 0, file: module_sp->GetFileSpec(), full: false) != UINT32_MAX; |
515 | } |
516 | |
517 | bool SearchFilterByModuleList::ModulePasses(const FileSpec &spec) { |
518 | if (m_module_spec_list.GetSize() == 0) |
519 | return true; |
520 | |
521 | return m_module_spec_list.FindFileIndex(idx: 0, file: spec, full: true) != UINT32_MAX; |
522 | } |
523 | |
524 | bool SearchFilterByModuleList::AddressPasses(Address &address) { |
525 | // FIXME: Not yet implemented |
526 | return true; |
527 | } |
528 | |
529 | void SearchFilterByModuleList::Search(Searcher &searcher) { |
530 | if (!m_target_sp) |
531 | return; |
532 | |
533 | if (searcher.GetDepth() == lldb::eSearchDepthTarget) { |
534 | SymbolContext empty_sc; |
535 | empty_sc.target_sp = m_target_sp; |
536 | searcher.SearchCallback(filter&: *this, context&: empty_sc, addr: nullptr); |
537 | } |
538 | |
539 | // If the module file spec is a full path, then we can just find the one |
540 | // filespec that passes. Otherwise, we need to go through all modules and |
541 | // find the ones that match the file name. |
542 | for (ModuleSP module_sp : m_target_sp->GetImages().Modules()) { |
543 | if (m_module_spec_list.FindFileIndex(idx: 0, file: module_sp->GetFileSpec(), full: false) == |
544 | UINT32_MAX) |
545 | continue; |
546 | SymbolContext matchingContext(m_target_sp, module_sp); |
547 | Searcher::CallbackReturn shouldContinue; |
548 | |
549 | shouldContinue = DoModuleIteration(context: matchingContext, searcher); |
550 | if (shouldContinue == Searcher::eCallbackReturnStop) |
551 | return; |
552 | } |
553 | } |
554 | |
555 | void SearchFilterByModuleList::GetDescription(Stream *s) { |
556 | size_t num_modules = m_module_spec_list.GetSize(); |
557 | if (num_modules == 1) { |
558 | s->Printf(format: ", module = " ); |
559 | s->PutCString( |
560 | cstr: m_module_spec_list.GetFileSpecAtIndex(idx: 0).GetFilename().AsCString( |
561 | value_if_empty: "<Unknown>" )); |
562 | return; |
563 | } |
564 | |
565 | s->Printf(format: ", modules(%" PRIu64 ") = " , (uint64_t)num_modules); |
566 | for (size_t i = 0; i < num_modules; i++) { |
567 | s->PutCString( |
568 | cstr: m_module_spec_list.GetFileSpecAtIndex(idx: i).GetFilename().AsCString( |
569 | value_if_empty: "<Unknown>" )); |
570 | if (i != num_modules - 1) |
571 | s->PutCString(cstr: ", " ); |
572 | } |
573 | } |
574 | |
575 | uint32_t SearchFilterByModuleList::GetFilterRequiredItems() { |
576 | return eSymbolContextModule; |
577 | } |
578 | |
579 | void SearchFilterByModuleList::Dump(Stream *s) const {} |
580 | |
581 | lldb::SearchFilterSP SearchFilterByModuleList::DoCreateCopy() { |
582 | return std::make_shared<SearchFilterByModuleList>(args&: *this); |
583 | } |
584 | |
585 | SearchFilterSP SearchFilterByModuleList::CreateFromStructuredData( |
586 | const lldb::TargetSP& target_sp, |
587 | const StructuredData::Dictionary &data_dict, |
588 | Status &error) { |
589 | StructuredData::Array *modules_array; |
590 | bool success = data_dict.GetValueForKeyAsArray(key: GetKey(enum_value: OptionNames::ModList), |
591 | result&: modules_array); |
592 | |
593 | if (!success) |
594 | return std::make_shared<SearchFilterByModuleList>(args: target_sp, |
595 | args: FileSpecList{}); |
596 | FileSpecList modules; |
597 | size_t num_modules = modules_array->GetSize(); |
598 | for (size_t i = 0; i < num_modules; i++) { |
599 | std::optional<llvm::StringRef> maybe_module = |
600 | modules_array->GetItemAtIndexAsString(idx: i); |
601 | if (!maybe_module) { |
602 | error.SetErrorStringWithFormat( |
603 | "SFBM::CFSD: filter module item %zu not a string." , i); |
604 | return nullptr; |
605 | } |
606 | modules.EmplaceBack(args&: *maybe_module); |
607 | } |
608 | return std::make_shared<SearchFilterByModuleList>(args: target_sp, args&: modules); |
609 | } |
610 | |
611 | void SearchFilterByModuleList::SerializeUnwrapped( |
612 | StructuredData::DictionarySP &options_dict_sp) { |
613 | SerializeFileSpecList(options_dict_sp, name: OptionNames::ModList, |
614 | file_list&: m_module_spec_list); |
615 | } |
616 | |
617 | StructuredData::ObjectSP SearchFilterByModuleList::SerializeToStructuredData() { |
618 | auto options_dict_sp = std::make_shared<StructuredData::Dictionary>(); |
619 | SerializeUnwrapped(options_dict_sp); |
620 | return WrapOptionsDict(options_dict_sp); |
621 | } |
622 | |
623 | // SearchFilterByModuleListAndCU: |
624 | // Selects a shared library matching a given file spec |
625 | |
626 | SearchFilterByModuleListAndCU::SearchFilterByModuleListAndCU( |
627 | const lldb::TargetSP &target_sp, const FileSpecList &module_list, |
628 | const FileSpecList &cu_list) |
629 | : SearchFilterByModuleList(target_sp, module_list, |
630 | FilterTy::ByModulesAndCU), |
631 | m_cu_spec_list(cu_list) {} |
632 | |
633 | SearchFilterByModuleListAndCU::~SearchFilterByModuleListAndCU() = default; |
634 | |
635 | lldb::SearchFilterSP SearchFilterByModuleListAndCU::CreateFromStructuredData( |
636 | const lldb::TargetSP& target_sp, |
637 | const StructuredData::Dictionary &data_dict, |
638 | Status &error) { |
639 | StructuredData::Array *modules_array = nullptr; |
640 | SearchFilterSP result_sp; |
641 | bool success = data_dict.GetValueForKeyAsArray(key: GetKey(enum_value: OptionNames::ModList), |
642 | result&: modules_array); |
643 | FileSpecList modules; |
644 | if (success) { |
645 | size_t num_modules = modules_array->GetSize(); |
646 | for (size_t i = 0; i < num_modules; i++) { |
647 | std::optional<llvm::StringRef> maybe_module = |
648 | modules_array->GetItemAtIndexAsString(idx: i); |
649 | if (!maybe_module) { |
650 | error.SetErrorStringWithFormat( |
651 | "SFBM::CFSD: filter module item %zu not a string." , i); |
652 | return result_sp; |
653 | } |
654 | modules.EmplaceBack(args&: *maybe_module); |
655 | } |
656 | } |
657 | |
658 | StructuredData::Array *cus_array = nullptr; |
659 | success = |
660 | data_dict.GetValueForKeyAsArray(key: GetKey(enum_value: OptionNames::CUList), result&: cus_array); |
661 | if (!success) { |
662 | error.SetErrorString("SFBM::CFSD: Could not find the CU list key." ); |
663 | return result_sp; |
664 | } |
665 | |
666 | size_t num_cus = cus_array->GetSize(); |
667 | FileSpecList cus; |
668 | for (size_t i = 0; i < num_cus; i++) { |
669 | std::optional<llvm::StringRef> maybe_cu = |
670 | cus_array->GetItemAtIndexAsString(idx: i); |
671 | if (!maybe_cu) { |
672 | error.SetErrorStringWithFormat( |
673 | "SFBM::CFSD: filter CU item %zu not a string." , i); |
674 | return nullptr; |
675 | } |
676 | cus.EmplaceBack(args&: *maybe_cu); |
677 | } |
678 | |
679 | return std::make_shared<SearchFilterByModuleListAndCU>( |
680 | args: target_sp, args&: modules, args&: cus); |
681 | } |
682 | |
683 | StructuredData::ObjectSP |
684 | SearchFilterByModuleListAndCU::SerializeToStructuredData() { |
685 | auto options_dict_sp = std::make_shared<StructuredData::Dictionary>(); |
686 | SearchFilterByModuleList::SerializeUnwrapped(options_dict_sp); |
687 | SerializeFileSpecList(options_dict_sp, name: OptionNames::CUList, file_list&: m_cu_spec_list); |
688 | return WrapOptionsDict(options_dict_sp); |
689 | } |
690 | |
691 | bool SearchFilterByModuleListAndCU::AddressPasses(Address &address) { |
692 | SymbolContext sym_ctx; |
693 | address.CalculateSymbolContext(sc: &sym_ctx, resolve_scope: eSymbolContextEverything); |
694 | if (!sym_ctx.comp_unit) { |
695 | if (m_cu_spec_list.GetSize() != 0) |
696 | return false; // Has no comp_unit so can't pass the file check. |
697 | } |
698 | FileSpec cu_spec; |
699 | if (sym_ctx.comp_unit) |
700 | cu_spec = sym_ctx.comp_unit->GetPrimaryFile(); |
701 | if (m_cu_spec_list.FindFileIndex(idx: 0, file: cu_spec, full: false) == UINT32_MAX) |
702 | return false; // Fails the file check |
703 | return SearchFilterByModuleList::ModulePasses(module_sp: sym_ctx.module_sp); |
704 | } |
705 | |
706 | bool SearchFilterByModuleListAndCU::CompUnitPasses(FileSpec &fileSpec) { |
707 | return m_cu_spec_list.FindFileIndex(idx: 0, file: fileSpec, full: false) != UINT32_MAX; |
708 | } |
709 | |
710 | bool SearchFilterByModuleListAndCU::CompUnitPasses(CompileUnit &compUnit) { |
711 | bool in_cu_list = m_cu_spec_list.FindFileIndex(idx: 0, file: compUnit.GetPrimaryFile(), |
712 | full: false) != UINT32_MAX; |
713 | if (!in_cu_list) |
714 | return false; |
715 | |
716 | ModuleSP module_sp(compUnit.GetModule()); |
717 | if (!module_sp) |
718 | return true; |
719 | |
720 | return SearchFilterByModuleList::ModulePasses(module_sp); |
721 | } |
722 | |
723 | void SearchFilterByModuleListAndCU::Search(Searcher &searcher) { |
724 | if (!m_target_sp) |
725 | return; |
726 | |
727 | if (searcher.GetDepth() == lldb::eSearchDepthTarget) { |
728 | SymbolContext empty_sc; |
729 | empty_sc.target_sp = m_target_sp; |
730 | searcher.SearchCallback(filter&: *this, context&: empty_sc, addr: nullptr); |
731 | } |
732 | |
733 | // If the module file spec is a full path, then we can just find the one |
734 | // filespec that passes. Otherwise, we need to go through all modules and |
735 | // find the ones that match the file name. |
736 | |
737 | ModuleList matching_modules; |
738 | |
739 | bool no_modules_in_filter = m_module_spec_list.GetSize() == 0; |
740 | for (ModuleSP module_sp : m_target_sp->GetImages().Modules()) { |
741 | if (!no_modules_in_filter && |
742 | m_module_spec_list.FindFileIndex(idx: 0, file: module_sp->GetFileSpec(), full: false) == |
743 | UINT32_MAX) |
744 | continue; |
745 | |
746 | SymbolContext matchingContext(m_target_sp, module_sp); |
747 | Searcher::CallbackReturn shouldContinue; |
748 | |
749 | if (searcher.GetDepth() == lldb::eSearchDepthModule) { |
750 | shouldContinue = DoModuleIteration(context: matchingContext, searcher); |
751 | if (shouldContinue == Searcher::eCallbackReturnStop) |
752 | return; |
753 | continue; |
754 | } |
755 | |
756 | const size_t num_cu = module_sp->GetNumCompileUnits(); |
757 | for (size_t cu_idx = 0; cu_idx < num_cu; cu_idx++) { |
758 | CompUnitSP cu_sp = module_sp->GetCompileUnitAtIndex(idx: cu_idx); |
759 | matchingContext.comp_unit = cu_sp.get(); |
760 | if (!matchingContext.comp_unit) |
761 | continue; |
762 | if (m_cu_spec_list.FindFileIndex( |
763 | idx: 0, file: matchingContext.comp_unit->GetPrimaryFile(), full: false) == |
764 | UINT32_MAX) |
765 | continue; |
766 | shouldContinue = DoCUIteration(module_sp, context: matchingContext, searcher); |
767 | if (shouldContinue == Searcher::eCallbackReturnStop) |
768 | return; |
769 | } |
770 | } |
771 | } |
772 | |
773 | void SearchFilterByModuleListAndCU::GetDescription(Stream *s) { |
774 | size_t num_modules = m_module_spec_list.GetSize(); |
775 | if (num_modules == 1) { |
776 | s->Printf(format: ", module = " ); |
777 | s->PutCString( |
778 | cstr: m_module_spec_list.GetFileSpecAtIndex(idx: 0).GetFilename().AsCString( |
779 | value_if_empty: "<Unknown>" )); |
780 | } else if (num_modules > 0) { |
781 | s->Printf(format: ", modules(%" PRIu64 ") = " , static_cast<uint64_t>(num_modules)); |
782 | for (size_t i = 0; i < num_modules; i++) { |
783 | s->PutCString( |
784 | cstr: m_module_spec_list.GetFileSpecAtIndex(idx: i).GetFilename().AsCString( |
785 | value_if_empty: "<Unknown>" )); |
786 | if (i != num_modules - 1) |
787 | s->PutCString(cstr: ", " ); |
788 | } |
789 | } |
790 | } |
791 | |
792 | uint32_t SearchFilterByModuleListAndCU::GetFilterRequiredItems() { |
793 | return eSymbolContextModule | eSymbolContextCompUnit; |
794 | } |
795 | |
796 | void SearchFilterByModuleListAndCU::Dump(Stream *s) const {} |
797 | |
798 | SearchFilterSP SearchFilterByModuleListAndCU::DoCreateCopy() { |
799 | return std::make_shared<SearchFilterByModuleListAndCU>(args&: *this); |
800 | } |
801 | |