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
33namespace lldb_private {
34class Address;
35}
36namespace lldb_private {
37class Function;
38}
39
40using namespace lldb;
41using namespace lldb_private;
42
43const char *SearchFilter::g_ty_to_name[] = {"Unconstrained", "Exception",
44                                            "Module",        "Modules",
45                                            "ModulesAndCU",  "Unknown"};
46
47const char
48    *SearchFilter::g_option_names[SearchFilter::OptionNames::LastOptionName] = {
49        "ModuleList", "CUList"};
50
51const 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
58SearchFilter::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
66Searcher::Searcher() = default;
67
68Searcher::~Searcher() = default;
69
70void Searcher::GetDescription(Stream *s) {}
71
72SearchFilter::SearchFilter(const TargetSP &target_sp, unsigned char filterType)
73    : m_target_sp(target_sp), SubclassID(filterType) {}
74
75SearchFilter::~SearchFilter() = default;
76
77SearchFilterSP 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      GetSerializationSubclassKey(), subclass_name);
91  if (!success) {
92    error.SetErrorString("Filter data missing subclass key");
93    return result_sp;
94  }
95
96  FilterTy filter_type = NameToFilterTy(subclass_name);
97  if (filter_type == UnknownFilter) {
98    error.SetErrorStringWithFormatv("Unknown filter type: {0}.", subclass_name);
99    return result_sp;
100  }
101
102  StructuredData::Dictionary *subclass_options = nullptr;
103  success = filter_dict.GetValueForKeyAsDictionary(
104      GetSerializationSubclassOptionsKey(), 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, *subclass_options, error);
114    break;
115  case ByModule:
116    result_sp = SearchFilterByModule::CreateFromStructuredData(
117        target_sp, *subclass_options, error);
118    break;
119  case ByModules:
120    result_sp = SearchFilterByModuleList::CreateFromStructuredData(
121        target_sp, *subclass_options, error);
122    break;
123  case ByModulesAndCU:
124    result_sp = SearchFilterByModuleListAndCU::CreateFromStructuredData(
125        target_sp, *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
137bool SearchFilter::ModulePasses(const FileSpec &spec) { return true; }
138
139bool SearchFilter::ModulePasses(const ModuleSP &module_sp) { return true; }
140
141bool SearchFilter::AddressPasses(Address &address) { return true; }
142
143bool SearchFilter::CompUnitPasses(FileSpec &fileSpec) { return true; }
144
145bool SearchFilter::CompUnitPasses(CompileUnit &compUnit) { return true; }
146
147bool 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(addr);
153}
154
155
156uint32_t SearchFilter::GetFilterRequiredItems() {
157  return (lldb::SymbolContextItem)0;
158}
159
160void SearchFilter::GetDescription(Stream *s) {}
161
162void SearchFilter::Dump(Stream *s) const {}
163
164lldb::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
172StructuredData::DictionarySP
173SearchFilter::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(GetSerializationSubclassKey(), GetFilterName());
179  type_dict_sp->AddItem(GetSerializationSubclassOptionsKey(), options_dict_sp);
180
181  return type_dict_sp;
182}
183
184void 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(std::make_shared<StructuredData::String>(
196        file_list.GetFileSpecAtIndex(i).GetPath()));
197  }
198  options_dict_sp->AddItem(GetKey(name), module_array_sp);
199}
200
201// UTILITY Functions to help iterate down through the elements of the
202// SymbolContext.
203
204void 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(*this, empty_sc, nullptr);
213    return;
214  }
215
216  DoModuleIteration(empty_sc, searcher);
217}
218
219void 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(*this, empty_sc, 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
239Searcher::CallbackReturn
240SearchFilter::DoModuleIteration(const lldb::ModuleSP &module_sp,
241                                Searcher &searcher) {
242  SymbolContext matchingContext(m_target_sp, module_sp);
243  return DoModuleIteration(matchingContext, searcher);
244}
245
246Searcher::CallbackReturn
247SearchFilter::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(context.module_sp, context, searcher);
255
256    SymbolContext matchingContext(context.module_sp.get());
257    searcher.SearchCallback(*this, matchingContext, 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(*this, matchingContext, 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
288Searcher::CallbackReturn
289SearchFilter::DoCUIteration(const ModuleSP &module_sp,
290                            const SymbolContext &context, Searcher &searcher) {
291  Searcher::CallbackReturn shouldContinue;
292  if (context.comp_unit != nullptr) {
293    if (CompUnitPasses(*context.comp_unit)) {
294      SymbolContext matchingContext(m_target_sp, module_sp, context.comp_unit);
295      return searcher.SearchCallback(*this, matchingContext, 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(i));
303    if (!cu_sp)
304      continue;
305    if (!CompUnitPasses(*(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(*this, matchingContext, 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(*cu_sp))
327      continue;
328    // If we got any functions, use ForeachFunction to do the iteration.
329    cu_sp->ForeachFunction([&](const FunctionSP &func_sp) {
330      if (!FunctionPasses(*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(*this, matchingContext, nullptr);
337      } else {
338        shouldContinue = DoFunctionIteration(func_sp.get(), context, searcher);
339      }
340      return shouldContinue != Searcher::eCallbackReturnContinue;
341    });
342  }
343  return Searcher::eCallbackReturnContinue;
344}
345
346Searcher::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".
355SearchFilterSP 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>(target_sp);
361}
362
363StructuredData::ObjectSP
364SearchFilterForUnconstrainedSearches::SerializeToStructuredData() {
365  // The options dictionary is an empty dictionary:
366  auto result_sp = std::make_shared<StructuredData::Dictionary>();
367  return WrapOptionsDict(result_sp);
368}
369
370bool SearchFilterForUnconstrainedSearches::ModulePasses(
371    const FileSpec &module_spec) {
372  return !m_target_sp->ModuleIsExcludedForUnconstrainedSearches(module_spec);
373}
374
375bool 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
384SearchFilterSP SearchFilterForUnconstrainedSearches::DoCreateCopy() {
385  return std::make_shared<SearchFilterForUnconstrainedSearches>(*this);
386}
387
388//  SearchFilterByModule:
389//  Selects a shared library matching a given file spec
390
391SearchFilterByModule::SearchFilterByModule(const lldb::TargetSP &target_sp,
392                                           const FileSpec &module)
393    : SearchFilter(target_sp, FilterTy::ByModule), m_module_spec(module) {}
394
395SearchFilterByModule::~SearchFilterByModule() = default;
396
397bool SearchFilterByModule::ModulePasses(const ModuleSP &module_sp) {
398  return (module_sp &&
399          FileSpec::Match(m_module_spec, module_sp->GetFileSpec()));
400}
401
402bool SearchFilterByModule::ModulePasses(const FileSpec &spec) {
403  return FileSpec::Match(m_module_spec, spec);
404}
405
406bool SearchFilterByModule::AddressPasses(Address &address) {
407  // FIXME: Not yet implemented
408  return true;
409}
410
411void 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(*this, empty_sc, 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(m_module_spec, module_sp->GetFileSpec())) {
430      SymbolContext matchingContext(m_target_sp, module_sp);
431      Searcher::CallbackReturn shouldContinue;
432
433      shouldContinue = DoModuleIteration(matchingContext, searcher);
434      if (shouldContinue == Searcher::eCallbackReturnStop)
435        return;
436    }
437  }
438}
439
440void SearchFilterByModule::GetDescription(Stream *s) {
441  s->PutCString(", module = ");
442  s->PutCString(m_module_spec.GetFilename().AsCString("<Unknown>"));
443}
444
445uint32_t SearchFilterByModule::GetFilterRequiredItems() {
446  return eSymbolContextModule;
447}
448
449void SearchFilterByModule::Dump(Stream *s) const {}
450
451SearchFilterSP SearchFilterByModule::DoCreateCopy() {
452  return std::make_shared<SearchFilterByModule>(*this);
453}
454
455SearchFilterSP 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(GetKey(OptionNames::ModList),
461                                                 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(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>(target_sp, module_spec);
483}
484
485StructuredData::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      std::make_shared<StructuredData::String>(m_module_spec.GetPath()));
490  options_dict_sp->AddItem(GetKey(OptionNames::ModList), module_array_sp);
491  return WrapOptionsDict(options_dict_sp);
492}
493
494//  SearchFilterByModuleList:
495//  Selects a shared library matching a given file spec
496
497SearchFilterByModuleList::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
502SearchFilterByModuleList::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
507SearchFilterByModuleList::~SearchFilterByModuleList() = default;
508
509bool 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                          0, module_sp->GetFileSpec(), false) != UINT32_MAX;
515}
516
517bool SearchFilterByModuleList::ModulePasses(const FileSpec &spec) {
518  if (m_module_spec_list.GetSize() == 0)
519    return true;
520
521  return m_module_spec_list.FindFileIndex(0, spec, true) != UINT32_MAX;
522}
523
524bool SearchFilterByModuleList::AddressPasses(Address &address) {
525  // FIXME: Not yet implemented
526  return true;
527}
528
529void 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(*this, empty_sc, 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(0, module_sp->GetFileSpec(), false) ==
544        UINT32_MAX)
545      continue;
546    SymbolContext matchingContext(m_target_sp, module_sp);
547    Searcher::CallbackReturn shouldContinue;
548
549    shouldContinue = DoModuleIteration(matchingContext, searcher);
550    if (shouldContinue == Searcher::eCallbackReturnStop)
551      return;
552  }
553}
554
555void SearchFilterByModuleList::GetDescription(Stream *s) {
556  size_t num_modules = m_module_spec_list.GetSize();
557  if (num_modules == 1) {
558    s->Printf(", module = ");
559    s->PutCString(
560        m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString(
561            "<Unknown>"));
562    return;
563  }
564
565  s->Printf(", modules(%" PRIu64 ") = ", (uint64_t)num_modules);
566  for (size_t i = 0; i < num_modules; i++) {
567    s->PutCString(
568        m_module_spec_list.GetFileSpecAtIndex(i).GetFilename().AsCString(
569            "<Unknown>"));
570    if (i != num_modules - 1)
571      s->PutCString(", ");
572  }
573}
574
575uint32_t SearchFilterByModuleList::GetFilterRequiredItems() {
576  return eSymbolContextModule;
577}
578
579void SearchFilterByModuleList::Dump(Stream *s) const {}
580
581lldb::SearchFilterSP SearchFilterByModuleList::DoCreateCopy() {
582  return std::make_shared<SearchFilterByModuleList>(*this);
583}
584
585SearchFilterSP 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(GetKey(OptionNames::ModList),
591                                                 modules_array);
592
593  if (!success)
594    return std::make_shared<SearchFilterByModuleList>(target_sp,
595                                                      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(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(*maybe_module);
607  }
608  return std::make_shared<SearchFilterByModuleList>(target_sp, modules);
609}
610
611void SearchFilterByModuleList::SerializeUnwrapped(
612    StructuredData::DictionarySP &options_dict_sp) {
613  SerializeFileSpecList(options_dict_sp, OptionNames::ModList,
614                        m_module_spec_list);
615}
616
617StructuredData::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
626SearchFilterByModuleListAndCU::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
633SearchFilterByModuleListAndCU::~SearchFilterByModuleListAndCU() = default;
634
635lldb::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(GetKey(OptionNames::ModList),
642                                                 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(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(*maybe_module);
655    }
656  }
657
658  StructuredData::Array *cus_array = nullptr;
659  success =
660      data_dict.GetValueForKeyAsArray(GetKey(OptionNames::CUList), 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(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(*maybe_cu);
677  }
678
679  return std::make_shared<SearchFilterByModuleListAndCU>(
680      target_sp, modules, cus);
681}
682
683StructuredData::ObjectSP
684SearchFilterByModuleListAndCU::SerializeToStructuredData() {
685  auto options_dict_sp = std::make_shared<StructuredData::Dictionary>();
686  SearchFilterByModuleList::SerializeUnwrapped(options_dict_sp);
687  SerializeFileSpecList(options_dict_sp, OptionNames::CUList, m_cu_spec_list);
688  return WrapOptionsDict(options_dict_sp);
689}
690
691bool SearchFilterByModuleListAndCU::AddressPasses(Address &address) {
692  SymbolContext sym_ctx;
693  address.CalculateSymbolContext(&sym_ctx, 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(0, cu_spec, false) == UINT32_MAX)
702    return false; // Fails the file check
703  return SearchFilterByModuleList::ModulePasses(sym_ctx.module_sp);
704}
705
706bool SearchFilterByModuleListAndCU::CompUnitPasses(FileSpec &fileSpec) {
707  return m_cu_spec_list.FindFileIndex(0, fileSpec, false) != UINT32_MAX;
708}
709
710bool SearchFilterByModuleListAndCU::CompUnitPasses(CompileUnit &compUnit) {
711  bool in_cu_list = m_cu_spec_list.FindFileIndex(0, compUnit.GetPrimaryFile(),
712                                                 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
723void 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(*this, empty_sc, 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(0, module_sp->GetFileSpec(), 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(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(cu_idx);
759      matchingContext.comp_unit = cu_sp.get();
760      if (!matchingContext.comp_unit)
761        continue;
762      if (m_cu_spec_list.FindFileIndex(
763              0, matchingContext.comp_unit->GetPrimaryFile(), false) ==
764          UINT32_MAX)
765        continue;
766      shouldContinue = DoCUIteration(module_sp, matchingContext, searcher);
767      if (shouldContinue == Searcher::eCallbackReturnStop)
768        return;
769    }
770  }
771}
772
773void SearchFilterByModuleListAndCU::GetDescription(Stream *s) {
774  size_t num_modules = m_module_spec_list.GetSize();
775  if (num_modules == 1) {
776    s->Printf(", module = ");
777    s->PutCString(
778        m_module_spec_list.GetFileSpecAtIndex(0).GetFilename().AsCString(
779            "<Unknown>"));
780  } else if (num_modules > 0) {
781    s->Printf(", modules(%" PRIu64 ") = ", static_cast<uint64_t>(num_modules));
782    for (size_t i = 0; i < num_modules; i++) {
783      s->PutCString(
784          m_module_spec_list.GetFileSpecAtIndex(i).GetFilename().AsCString(
785              "<Unknown>"));
786      if (i != num_modules - 1)
787        s->PutCString(", ");
788    }
789  }
790}
791
792uint32_t SearchFilterByModuleListAndCU::GetFilterRequiredItems() {
793  return eSymbolContextModule | eSymbolContextCompUnit;
794}
795
796void SearchFilterByModuleListAndCU::Dump(Stream *s) const {}
797
798SearchFilterSP SearchFilterByModuleListAndCU::DoCreateCopy() {
799  return std::make_shared<SearchFilterByModuleListAndCU>(*this);
800}
801