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