1//===- SSAFLinker.cpp - SSAF Linker ---------------------------------------===//
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// This file implements the SSAF entity linker tool that performs entity
10// linking across multiple TU summaries using the EntityLinker framework.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/EntityLinker.h"
15#include "clang/ScalableStaticAnalysisFramework/Core/EntityLinker/TUSummaryEncoding.h"
16#include "clang/ScalableStaticAnalysisFramework/Core/Model/BuildNamespace.h"
17#include "clang/ScalableStaticAnalysisFramework/Core/Support/ErrorBuilder.h"
18#include "clang/ScalableStaticAnalysisFramework/SSAFForceLinker.h" // IWYU pragma: keep
19#include "clang/ScalableStaticAnalysisFramework/Tool/Utils.h"
20#include "llvm/ADT/STLExtras.h"
21#include "llvm/ADT/SmallVector.h"
22#include "llvm/Support/CommandLine.h"
23#include "llvm/Support/FileSystem.h"
24#include "llvm/Support/FormatVariadic.h"
25#include "llvm/Support/InitLLVM.h"
26#include "llvm/Support/Path.h"
27#include "llvm/Support/Timer.h"
28#include "llvm/Support/WithColor.h"
29#include "llvm/Support/raw_ostream.h"
30#include <memory>
31#include <string>
32#include <system_error>
33
34using namespace llvm;
35using namespace clang::ssaf;
36
37namespace fs = llvm::sys::fs;
38namespace path = llvm::sys::path;
39
40namespace {
41
42//===----------------------------------------------------------------------===//
43// Command-Line Options
44//===----------------------------------------------------------------------===//
45
46cl::OptionCategory SsafLinkerCategory("clang-ssaf-linker options");
47
48cl::list<std::string> InputPaths(cl::Positional, cl::desc("<input files>"),
49 cl::OneOrMore, cl::cat(SsafLinkerCategory));
50
51cl::opt<std::string> OutputPath("o", cl::desc("Output summary path"),
52 cl::value_desc("path"), cl::Required,
53 cl::cat(SsafLinkerCategory));
54
55cl::opt<bool> Verbose("verbose", cl::desc("Enable verbose output"),
56 cl::init(Val: false), cl::cat(SsafLinkerCategory));
57
58cl::opt<bool> Time("time", cl::desc("Enable timing"), cl::init(Val: false),
59 cl::cat(SsafLinkerCategory));
60
61//===----------------------------------------------------------------------===//
62// Error Messages
63//===----------------------------------------------------------------------===//
64
65namespace LocalErrorMessages {
66
67constexpr const char *OutputDirectoryNotWritable =
68 "Parent directory is not writable";
69
70constexpr const char *LinkingSummary = "Linking summary '{0}'";
71
72} // namespace LocalErrorMessages
73
74//===----------------------------------------------------------------------===//
75// Diagnostic Utilities
76//===----------------------------------------------------------------------===//
77
78constexpr unsigned IndentationWidth = 2;
79
80template <typename... Ts>
81void info(unsigned IndentationLevel, const char *Fmt, Ts &&...Args) {
82 if (Verbose) {
83 llvm::WithColor::note()
84 << std::string(IndentationLevel * IndentationWidth, ' ') << "- "
85 << llvm::formatv(Fmt, std::forward<Ts>(Args)...) << "\n";
86 }
87}
88
89//===----------------------------------------------------------------------===//
90// Data Structures
91//===----------------------------------------------------------------------===//
92
93struct LinkerInput {
94 std::vector<SummaryFile> InputFiles;
95 SummaryFile OutputFile;
96 std::string LinkUnitName;
97};
98
99//===----------------------------------------------------------------------===//
100// Pipeline
101//===----------------------------------------------------------------------===//
102
103LinkerInput validate(llvm::TimerGroup &TG) {
104 llvm::Timer TValidate("validate", "Validate Input", TG);
105 LinkerInput LI;
106
107 {
108 llvm::TimeRegion _(Time ? &TValidate : nullptr);
109 llvm::StringRef ParentDir = path::parent_path(path: OutputPath);
110 llvm::StringRef DirToCheck = ParentDir.empty() ? "." : ParentDir;
111
112 if (!fs::exists(Path: DirToCheck)) {
113 fail(Fmt: ErrorMessages::CannotValidateSummary, Args&: OutputPath,
114 Args: ErrorMessages::OutputDirectoryMissing);
115 }
116
117 if (fs::access(Path: DirToCheck, Mode: fs::AccessMode::Write)) {
118 fail(Fmt: ErrorMessages::CannotValidateSummary, Args&: OutputPath,
119 Args: LocalErrorMessages::OutputDirectoryNotWritable);
120 }
121
122 LI.OutputFile = SummaryFile::fromPath(Path: OutputPath);
123 LI.LinkUnitName = path::stem(path: LI.OutputFile.Path).str();
124 }
125
126 info(IndentationLevel: 2, Fmt: "Validated output summary path '{0}'.", Args&: LI.OutputFile.Path);
127
128 {
129 llvm::TimeRegion _(Time ? &TValidate : nullptr);
130 for (const auto &InputPath : InputPaths) {
131 llvm::SmallString<256> RealPath;
132 std::error_code EC = fs::real_path(path: InputPath, output&: RealPath, expand_tilde: true);
133 if (EC) {
134 fail(Fmt: ErrorMessages::CannotValidateSummary, Args: InputPath, Args: EC.message());
135 }
136 LI.InputFiles.push_back(x: SummaryFile::fromPath(Path: RealPath));
137 }
138 }
139
140 info(IndentationLevel: 2, Fmt: "Validated {0} input summary paths.", Args: LI.InputFiles.size());
141
142 return LI;
143}
144
145void link(const LinkerInput &LI, llvm::TimerGroup &TG) {
146 info(IndentationLevel: 2, Fmt: "Constructing linker.");
147
148 EntityLinker EL(NestedBuildNamespace(
149 BuildNamespace(BuildNamespaceKind::LinkUnit, LI.LinkUnitName)));
150
151 llvm::Timer TRead("read", "Read Summaries", TG);
152 llvm::Timer TLink("link", "Link Summaries", TG);
153 llvm::Timer TWrite("write", "Write Summary", TG);
154
155 info(IndentationLevel: 2, Fmt: "Linking summaries.");
156
157 for (auto [Index, InputFile] : llvm::enumerate(First: LI.InputFiles)) {
158 std::unique_ptr<TUSummaryEncoding> Summary;
159
160 {
161 info(IndentationLevel: 3, Fmt: "[{0}/{1}] Reading '{2}'.", Args: (Index + 1), Args: LI.InputFiles.size(),
162 Args: InputFile.Path);
163
164 llvm::TimeRegion _(Time ? &TRead : nullptr);
165
166 auto ExpectedSummaryEncoding =
167 InputFile.Format->readTUSummaryEncoding(Path: InputFile.Path);
168 if (!ExpectedSummaryEncoding) {
169 fail(Err: ExpectedSummaryEncoding.takeError());
170 }
171
172 Summary = std::make_unique<TUSummaryEncoding>(
173 args: std::move(*ExpectedSummaryEncoding));
174 }
175
176 {
177 info(IndentationLevel: 3, Fmt: "[{0}/{1}] Linking '{2}'.", Args: (Index + 1), Args: LI.InputFiles.size(),
178 Args: InputFile.Path);
179
180 llvm::TimeRegion _(Time ? &TLink : nullptr);
181
182 if (auto Err = EL.link(Summary: std::move(Summary))) {
183 fail(Err: ErrorBuilder::wrap(E: std::move(Err))
184 .context(Fmt: LocalErrorMessages::LinkingSummary, ArgVals: InputFile.Path)
185 .build());
186 }
187 }
188 }
189
190 {
191 info(IndentationLevel: 2, Fmt: "Writing output summary to '{0}'.", Args: LI.OutputFile.Path);
192
193 llvm::TimeRegion _(Time ? &TWrite : nullptr);
194
195 auto Output = std::move(EL).getOutput();
196 if (auto Err = LI.OutputFile.Format->writeLUSummaryEncoding(
197 SummaryEncoding: Output, Path: LI.OutputFile.Path)) {
198 fail(Err: std::move(Err));
199 }
200 }
201}
202
203} // namespace
204
205//===----------------------------------------------------------------------===//
206// Driver
207//===----------------------------------------------------------------------===//
208
209int main(int argc, const char **argv) {
210 llvm::StringRef ToolHeading = "SSAF Linker";
211
212 InitLLVM X(argc, argv);
213 initTool(argc, argv, Version: "0.1", Category&: SsafLinkerCategory, ToolHeading);
214
215 llvm::TimerGroup LinkerTimers(getToolName(), ToolHeading);
216 LinkerInput LI;
217
218 {
219 info(IndentationLevel: 0, Fmt: "Linking started.");
220
221 {
222 info(IndentationLevel: 1, Fmt: "Validating input.");
223 LI = validate(TG&: LinkerTimers);
224 }
225
226 {
227 info(IndentationLevel: 1, Fmt: "Linking input.");
228 link(LI, TG&: LinkerTimers);
229 }
230
231 info(IndentationLevel: 0, Fmt: "Linking finished.");
232 }
233
234 return 0;
235}
236