1//===- TUSummaryExtractorFrontendAction.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 "clang/ScalableStaticAnalysis/Frontend/TUSummaryExtractorFrontendAction.h"
10#include "clang/AST/ASTConsumer.h"
11#include "clang/AST/ASTContext.h"
12#include "clang/Basic/DiagnosticFrontend.h"
13#include "clang/Basic/TargetInfo.h"
14#include "clang/Frontend/MultiplexConsumer.h"
15#include "clang/Frontend/SSAFOptions.h"
16#include "clang/ScalableStaticAnalysis/Core/Serialization/SerializationFormatRegistry.h"
17#include "clang/ScalableStaticAnalysis/Core/TUSummary/ExtractorRegistry.h"
18#include "clang/ScalableStaticAnalysis/Core/TUSummary/TUSummary.h"
19#include "clang/ScalableStaticAnalysis/Core/TUSummary/TUSummaryBuilder.h"
20#include "clang/ScalableStaticAnalysis/Core/TUSummary/TUSummaryExtractor.h"
21#include "llvm/ADT/StringExtras.h"
22#include "llvm/ADT/StringRef.h"
23#include "llvm/Support/IOSandbox.h"
24#include "llvm/Support/Path.h"
25#include "llvm/TargetParser/Triple.h"
26#include <memory>
27#include <string>
28#include <vector>
29
30using namespace clang;
31using namespace ssaf;
32
33static std::optional<std::pair<llvm::StringRef, llvm::StringRef>>
34parseOutputFileFormatAndPathOrReportError(DiagnosticsEngine &Diags,
35 StringRef SSAFTUSummaryFile) {
36
37 StringRef Ext = llvm::sys::path::extension(path: SSAFTUSummaryFile);
38 StringRef FilePath = SSAFTUSummaryFile.drop_back(N: Ext.size());
39
40 if (!Ext.consume_front(Prefix: ".") || FilePath.empty()) {
41 Diags.Report(DiagID: diag::warn_ssaf_extract_tu_summary_file_unknown_format)
42 << SSAFTUSummaryFile;
43 return std::nullopt;
44 }
45
46 if (!isFormatRegistered(FormatName: Ext)) {
47 Diags.Report(DiagID: diag::warn_ssaf_extract_tu_summary_file_unknown_output_format)
48 << Ext << SSAFTUSummaryFile;
49 return std::nullopt;
50 }
51
52 return std::pair{Ext, FilePath};
53}
54
55/// Return \c true if reported unrecognized extractors.
56static bool
57reportUnrecognizedExtractorNames(DiagnosticsEngine &Diags,
58 ArrayRef<std::string> SSAFExtractSummaries) {
59 if (SSAFExtractSummaries.empty()) {
60 Diags.Report(DiagID: diag::warn_ssaf_must_enable_summary_extractors);
61 return true;
62 }
63
64 std::vector<StringRef> UnrecognizedExtractorNames;
65 for (StringRef Name : SSAFExtractSummaries)
66 if (!isTUSummaryExtractorRegistered(SummaryName: Name))
67 UnrecognizedExtractorNames.push_back(x: Name);
68
69 if (!UnrecognizedExtractorNames.empty()) {
70 Diags.Report(DiagID: diag::warn_ssaf_extract_summary_unknown_extractor_name)
71 << UnrecognizedExtractorNames.size()
72 << llvm::join(R&: UnrecognizedExtractorNames, Separator: ", ");
73 return true;
74 }
75
76 return false;
77}
78
79static std::vector<std::unique_ptr<ASTConsumer>>
80makeTUSummaryExtractors(TUSummaryBuilder &Builder,
81 ArrayRef<std::string> SSAFExtractSummaries) {
82 std::vector<std::unique_ptr<ASTConsumer>> Extractors;
83 Extractors.reserve(n: SSAFExtractSummaries.size());
84 for (StringRef Name : SSAFExtractSummaries) {
85 assert(isTUSummaryExtractorRegistered(Name));
86 Extractors.push_back(x: makeTUSummaryExtractor(SummaryName: Name, Builder));
87 }
88 return Extractors;
89}
90
91namespace {
92
93/// Drives all extractor \c ASTConsumers and serializes the completed
94/// \c TUSummary.
95///
96/// Derives from \c MultiplexConsumer so every \c ASTConsumer virtual method is
97/// automatically forwarded to each extractor.
98class TUSummaryRunner final : public MultiplexConsumer {
99public:
100 static std::unique_ptr<TUSummaryRunner> create(CompilerInstance &CI);
101
102private:
103 TUSummaryRunner(llvm::Triple TargetTriple,
104 std::unique_ptr<SerializationFormat> Format,
105 const SSAFOptions &Opts);
106
107 void HandleTranslationUnit(ASTContext &Ctx) override;
108
109 TUSummary Summary;
110
111 /// Owned by the \c CompilerInstance.
112 const SSAFOptions &Opts;
113
114 TUSummaryBuilder Builder = TUSummaryBuilder(Summary, Opts);
115 std::unique_ptr<SerializationFormat> Format;
116};
117} // namespace
118
119std::unique_ptr<TUSummaryRunner> TUSummaryRunner::create(CompilerInstance &CI) {
120 const SSAFOptions &Opts = CI.getSSAFOpts();
121 DiagnosticsEngine &Diags = CI.getDiagnostics();
122
123 if (Opts.CompilationUnitId.empty()) {
124 Diags.Report(DiagID: diag::warn_ssaf_tu_summary_requires_compilation_unit_id);
125 return nullptr;
126 }
127
128 auto MaybePair =
129 parseOutputFileFormatAndPathOrReportError(Diags, SSAFTUSummaryFile: Opts.TUSummaryFile);
130 if (!MaybePair.has_value())
131 return nullptr;
132 auto [FormatName, OutputPath] = MaybePair.value();
133
134 if (reportUnrecognizedExtractorNames(Diags, SSAFExtractSummaries: Opts.ExtractSummaries))
135 return nullptr;
136
137 return std::unique_ptr<TUSummaryRunner>{new TUSummaryRunner{
138 CI.getTarget().getTriple(), makeFormat(FormatName), Opts}};
139}
140
141TUSummaryRunner::TUSummaryRunner(llvm::Triple TargetTriple,
142 std::unique_ptr<SerializationFormat> Format,
143 const SSAFOptions &Opts)
144 : MultiplexConsumer(std::vector<std::unique_ptr<ASTConsumer>>{}),
145 Summary(std::move(TargetTriple),
146 BuildNamespace(BuildNamespaceKind::CompilationUnit,
147 Opts.CompilationUnitId)),
148 Opts(Opts), Format(std::move(Format)) {
149 assert(this->Format);
150 assert(!Opts.CompilationUnitId.empty());
151
152 // Now the Summary and the builders are constructed, we can also construct the
153 // extractors.
154 auto Extractors = makeTUSummaryExtractors(Builder, SSAFExtractSummaries: Opts.ExtractSummaries);
155 assert(!Extractors.empty());
156
157 // We must initialize the Consumers here because our extractors need a
158 // Builder that holds a reference to the TUSummary, which would be only
159 // initialized after the MultiplexConsumer ctor. This is the only way we can
160 // avoid the use of the TUSummary before it starts its lifetime.
161 MultiplexConsumer::Consumers = std::move(Extractors);
162}
163
164void TUSummaryRunner::HandleTranslationUnit(ASTContext &Ctx) {
165 // First, invoke the Summary Extractors.
166 MultiplexConsumer::HandleTranslationUnit(Ctx);
167
168 // FIXME(sandboxing): Remove this by adopting `llvm::vfs::OutputBackend`.
169 llvm::sys::sandbox::ScopedSetting Guard = llvm::sys::sandbox::scopedDisable();
170
171 // Then serialize the result.
172 if (auto Err = Format->writeTUSummary(Summary, Path: Opts.TUSummaryFile)) {
173 Ctx.getDiagnostics().Report(DiagID: diag::warn_ssaf_write_tu_summary_failed)
174 << Opts.TUSummaryFile << llvm::toString(E: std::move(Err));
175 }
176}
177
178TUSummaryExtractorFrontendAction::~TUSummaryExtractorFrontendAction() = default;
179
180TUSummaryExtractorFrontendAction::TUSummaryExtractorFrontendAction(
181 std::unique_ptr<FrontendAction> WrappedAction)
182 : WrapperFrontendAction(std::move(WrappedAction)) {}
183
184std::unique_ptr<ASTConsumer>
185TUSummaryExtractorFrontendAction::CreateASTConsumer(CompilerInstance &CI,
186 StringRef InFile) {
187 auto WrappedConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile);
188 if (!WrappedConsumer)
189 return nullptr;
190
191 if (auto Runner = TUSummaryRunner::create(CI)) {
192 CI.getCodeGenOpts().ClearASTBeforeBackend = false;
193 std::vector<std::unique_ptr<ASTConsumer>> Consumers;
194 Consumers.reserve(n: 2);
195 Consumers.push_back(x: std::move(WrappedConsumer));
196 Consumers.push_back(x: std::move(Runner));
197 return std::make_unique<MultiplexConsumer>(args: std::move(Consumers));
198 }
199 return WrappedConsumer;
200}
201