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