1//===--- ProfileList.h - ProfileList filter ---------------------*- 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// User-provided filters include/exclude profile instrumentation in certain
10// functions or files.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/Basic/ProfileList.h"
15#include "clang/Basic/FileManager.h"
16#include "clang/Basic/SourceManager.h"
17#include "llvm/Support/SpecialCaseList.h"
18
19#include "llvm/Support/raw_ostream.h"
20#include <optional>
21
22using namespace clang;
23
24namespace clang {
25
26class ProfileSpecialCaseList : public llvm::SpecialCaseList {
27public:
28  static std::unique_ptr<ProfileSpecialCaseList>
29  create(const std::vector<std::string> &Paths, llvm::vfs::FileSystem &VFS,
30         std::string &Error);
31
32  static std::unique_ptr<ProfileSpecialCaseList>
33  createOrDie(const std::vector<std::string> &Paths,
34              llvm::vfs::FileSystem &VFS);
35
36  bool isEmpty() const { return Sections.empty(); }
37
38  bool hasPrefix(StringRef Prefix) const {
39    for (auto &SectionIter : Sections)
40      if (SectionIter.Entries.count(Prefix) > 0)
41        return true;
42    return false;
43  }
44};
45
46std::unique_ptr<ProfileSpecialCaseList>
47ProfileSpecialCaseList::create(const std::vector<std::string> &Paths,
48                               llvm::vfs::FileSystem &VFS,
49                               std::string &Error) {
50  auto PSCL = std::make_unique<ProfileSpecialCaseList>();
51  if (PSCL->createInternal(Paths, VFS, Error))
52    return PSCL;
53  return nullptr;
54}
55
56std::unique_ptr<ProfileSpecialCaseList>
57ProfileSpecialCaseList::createOrDie(const std::vector<std::string> &Paths,
58                                    llvm::vfs::FileSystem &VFS) {
59  std::string Error;
60  if (auto PSCL = create(Paths, VFS, Error))
61    return PSCL;
62  llvm::report_fatal_error(llvm::Twine(Error));
63}
64
65}
66
67ProfileList::ProfileList(ArrayRef<std::string> Paths, SourceManager &SM)
68    : SCL(ProfileSpecialCaseList::createOrDie(
69          Paths, SM.getFileManager().getVirtualFileSystem())),
70      Empty(SCL->isEmpty()), SM(SM) {}
71
72ProfileList::~ProfileList() = default;
73
74static StringRef getSectionName(CodeGenOptions::ProfileInstrKind Kind) {
75  switch (Kind) {
76  case CodeGenOptions::ProfileNone:
77    return "";
78  case CodeGenOptions::ProfileClangInstr:
79    return "clang";
80  case CodeGenOptions::ProfileIRInstr:
81    return "llvm";
82  case CodeGenOptions::ProfileCSIRInstr:
83    return "csllvm";
84  }
85  llvm_unreachable("Unhandled CodeGenOptions::ProfileInstrKind enum");
86}
87
88ProfileList::ExclusionType
89ProfileList::getDefault(CodeGenOptions::ProfileInstrKind Kind) const {
90  StringRef Section = getSectionName(Kind);
91  // Check for "default:<type>"
92  if (SCL->inSection(Section, "default", "allow"))
93    return Allow;
94  if (SCL->inSection(Section, "default", "skip"))
95    return Skip;
96  if (SCL->inSection(Section, "default", "forbid"))
97    return Forbid;
98  // If any cases use "fun" or "src", set the default to FORBID.
99  if (SCL->hasPrefix("fun") || SCL->hasPrefix("src"))
100    return Forbid;
101  return Allow;
102}
103
104std::optional<ProfileList::ExclusionType>
105ProfileList::inSection(StringRef Section, StringRef Prefix,
106                       StringRef Query) const {
107  if (SCL->inSection(Section, Prefix, Query, "allow"))
108    return Allow;
109  if (SCL->inSection(Section, Prefix, Query, "skip"))
110    return Skip;
111  if (SCL->inSection(Section, Prefix, Query, "forbid"))
112    return Forbid;
113  if (SCL->inSection(Section, Prefix, Query))
114    return Allow;
115  return std::nullopt;
116}
117
118std::optional<ProfileList::ExclusionType>
119ProfileList::isFunctionExcluded(StringRef FunctionName,
120                                CodeGenOptions::ProfileInstrKind Kind) const {
121  StringRef Section = getSectionName(Kind);
122  // Check for "function:<regex>=<case>"
123  if (auto V = inSection(Section, "function", FunctionName))
124    return V;
125  if (SCL->inSection(Section, "!fun", FunctionName))
126    return Forbid;
127  if (SCL->inSection(Section, "fun", FunctionName))
128    return Allow;
129  return std::nullopt;
130}
131
132std::optional<ProfileList::ExclusionType>
133ProfileList::isLocationExcluded(SourceLocation Loc,
134                                CodeGenOptions::ProfileInstrKind Kind) const {
135  return isFileExcluded(SM.getFilename(SM.getFileLoc(Loc)), Kind);
136}
137
138std::optional<ProfileList::ExclusionType>
139ProfileList::isFileExcluded(StringRef FileName,
140                            CodeGenOptions::ProfileInstrKind Kind) const {
141  StringRef Section = getSectionName(Kind);
142  // Check for "source:<regex>=<case>"
143  if (auto V = inSection(Section, "source", FileName))
144    return V;
145  if (SCL->inSection(Section, "!src", FileName))
146    return Forbid;
147  if (SCL->inSection(Section, "src", FileName))
148    return Allow;
149  return std::nullopt;
150}
151