1//===- SSAFFormat.cpp - SSAF Format Tool ----------------------------------===//
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 format tool that validates and converts
10// TU and LU summaries between registered serialization formats.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/ScalableStaticAnalysis/Core/EntityLinker/LUSummaryEncoding.h"
15#include "clang/ScalableStaticAnalysis/Core/EntityLinker/MultiArchSharedLibrary.h"
16#include "clang/ScalableStaticAnalysis/Core/EntityLinker/MultiArchStaticLibrary.h"
17#include "clang/ScalableStaticAnalysis/Core/EntityLinker/StaticLibrary.h"
18#include "clang/ScalableStaticAnalysis/Core/EntityLinker/TUSummaryEncoding.h"
19#include "clang/ScalableStaticAnalysis/Core/Serialization/JSONFormat.h"
20#include "clang/ScalableStaticAnalysis/Core/Serialization/SerializationFormatRegistry.h"
21#include "clang/ScalableStaticAnalysis/SSAFForceLinker.h" // IWYU pragma: keep
22#include "clang/ScalableStaticAnalysis/Tool/Utils.h"
23#include "llvm/ADT/STLExtras.h"
24#include "llvm/ADT/SmallVector.h"
25#include "llvm/Support/CommandLine.h"
26#include "llvm/Support/ErrorHandling.h"
27#include "llvm/Support/Format.h"
28#include "llvm/Support/FormatVariadic.h"
29#include "llvm/Support/InitLLVM.h"
30#include "llvm/Support/Path.h"
31#include "llvm/Support/raw_ostream.h"
32#include <memory>
33#include <optional>
34#include <string>
35
36using namespace llvm;
37using namespace clang::ssaf;
38
39namespace {
40
41//===----------------------------------------------------------------------===//
42// Summary Type
43//===----------------------------------------------------------------------===//
44
45enum class SummaryType {
46 Auto,
47 TU,
48 LU,
49 StaticLibrary,
50 MultiArchStaticLibrary,
51 MultiArchSharedLibrary,
52 WPA
53};
54
55//===----------------------------------------------------------------------===//
56// Command-Line Options
57//===----------------------------------------------------------------------===//
58
59cl::OptionCategory SsafFormatCategory("clang-ssaf-format options");
60
61cl::list<std::string> LoadPlugins("load",
62 cl::desc("Load a plugin shared library"),
63 cl::value_desc("path"),
64 cl::cat(SsafFormatCategory));
65
66// Defaults to 'auto', which inspects the file's self-describing 'type'
67// field and dispatches to the matching reader/writer. Explicit values
68// force the use of the corresponding kind-specific reader/writer.
69cl::opt<SummaryType> Type(
70 "type",
71 cl::desc("Summary type (defaults to 'auto', which uses the file's "
72 "self-describing 'type' field)"),
73 cl::values(clEnumValN(SummaryType::Auto, "auto",
74 "Detect type from the file's 'type' field"),
75 clEnumValN(SummaryType::TU, "tu", "Translation unit summary"),
76 clEnumValN(SummaryType::LU, "lu", "Link unit summary"),
77 clEnumValN(SummaryType::StaticLibrary, "static-library",
78 "Static library of translation unit summaries"),
79 clEnumValN(SummaryType::MultiArchStaticLibrary,
80 "multi-arch-static-library",
81 "Multi-architecture static library"),
82 clEnumValN(SummaryType::MultiArchSharedLibrary,
83 "multi-arch-shared-library",
84 "Multi-architecture shared library"),
85 clEnumValN(SummaryType::WPA, "wpa",
86 "Whole-program analysis suite")),
87 cl::init(Val: SummaryType::Auto), cl::cat(SsafFormatCategory));
88
89cl::opt<std::string> InputPath(cl::Positional, cl::desc("<input file>"),
90 cl::cat(SsafFormatCategory));
91
92cl::opt<std::string> OutputPath("o", cl::desc("Output file path"),
93 cl::value_desc("path"),
94 cl::cat(SsafFormatCategory));
95
96cl::opt<bool> UseEncoding("encoding",
97 cl::desc("Read and write summary encodings rather "
98 "than decoded summaries"),
99 cl::cat(SsafFormatCategory));
100
101cl::opt<bool> ListFormats("list",
102 cl::desc("List registered serialization formats and "
103 "analyses, then exit"),
104 cl::init(Val: false), cl::cat(SsafFormatCategory));
105
106//===----------------------------------------------------------------------===//
107// Format Listing
108//===----------------------------------------------------------------------===//
109
110constexpr size_t FormatIndent = 4;
111constexpr size_t AnalysisIndent = 4;
112
113struct AnalysisData {
114 std::string Name;
115 std::string Desc;
116};
117
118struct FormatData {
119 std::string Name;
120 std::string Desc;
121 llvm::SmallVector<AnalysisData> Analyses;
122};
123
124struct PrintLayout {
125 size_t FormatNumWidth;
126 size_t MaxFormatNameWidth;
127 size_t FormatNameCol;
128 size_t AnalysisCol;
129 size_t AnalysisNumWidth;
130 size_t MaxAnalysisNameWidth;
131};
132
133llvm::SmallVector<FormatData> collectFormats() {
134 llvm::SmallVector<FormatData> Formats;
135 for (const auto &Entry : SerializationFormatRegistry::entries()) {
136 FormatData FD;
137 FD.Name = Entry.getName().str();
138 FD.Desc = Entry.getDesc().str();
139 auto Format = Entry.instantiate();
140 Format->forEachRegisteredAnalysis(
141 Callback: [&](llvm::StringRef Name, llvm::StringRef Desc) {
142 FD.Analyses.push_back(Elt: {.Name: Name.str(), .Desc: Desc.str()});
143 });
144 Formats.push_back(Elt: std::move(FD));
145 }
146 return Formats;
147}
148
149void printAnalysis(const AnalysisData &AD, size_t AnalysisIndex,
150 size_t FormatIndex, const PrintLayout &Layout) {
151 std::string AnalysisNum = std::to_string(val: FormatIndex + 1) + "." +
152 std::to_string(val: AnalysisIndex + 1) + ".";
153 llvm::outs().indent(NumSpaces: Layout.AnalysisCol)
154 << llvm::right_justify(Str: AnalysisNum, Width: Layout.AnalysisNumWidth) << " "
155 << llvm::left_justify(Str: AD.Name, Width: Layout.MaxAnalysisNameWidth) << " - "
156 << AD.Desc << "\n";
157}
158
159void printAnalyses(const llvm::SmallVector<AnalysisData> &Analyses,
160 size_t FormatIndex, const PrintLayout &Layout) {
161 if (Analyses.empty()) {
162 llvm::outs().indent(NumSpaces: Layout.FormatNameCol) << "Analyses: (none)\n";
163 return;
164 }
165
166 llvm::outs().indent(NumSpaces: Layout.FormatNameCol) << "Analyses:\n";
167
168 for (size_t AnalysisIndex = 0; AnalysisIndex < Analyses.size();
169 ++AnalysisIndex) {
170 printAnalysis(AD: Analyses[AnalysisIndex], AnalysisIndex, FormatIndex, Layout);
171 }
172}
173
174void printFormat(const FormatData &FD, size_t FormatIndex,
175 const PrintLayout &Layout) {
176 // Blank line before each format entry for readability.
177 llvm::outs() << "\n";
178
179 std::string FormatNum = std::to_string(val: FormatIndex + 1) + ".";
180 llvm::outs().indent(NumSpaces: FormatIndent)
181 << llvm::right_justify(Str: FormatNum, Width: Layout.FormatNumWidth) << " "
182 << llvm::left_justify(Str: FD.Name, Width: Layout.MaxFormatNameWidth) << " - "
183 << FD.Desc << "\n";
184
185 printAnalyses(Analyses: FD.Analyses, FormatIndex, Layout);
186}
187
188void printFormats(const llvm::SmallVector<FormatData> &Formats,
189 const PrintLayout &Layout) {
190 llvm::outs() << "Registered serialization formats:\n";
191 for (size_t FormatIndex = 0; FormatIndex < Formats.size(); ++FormatIndex) {
192 printFormat(FD: Formats[FormatIndex], FormatIndex, Layout);
193 }
194}
195
196PrintLayout computePrintLayout(const llvm::SmallVector<FormatData> &Formats) {
197 size_t MaxFormatNameWidth = 0;
198 size_t MaxAnalysisCount = 0;
199 size_t MaxAnalysisNameWidth = 0;
200 for (const auto &FD : Formats) {
201 MaxFormatNameWidth = std::max(a: MaxFormatNameWidth, b: FD.Name.size());
202 MaxAnalysisCount = std::max(a: MaxAnalysisCount, b: FD.Analyses.size());
203 for (const auto &AD : FD.Analyses) {
204 MaxAnalysisNameWidth = std::max(a: MaxAnalysisNameWidth, b: AD.Name.size());
205 }
206 }
207
208 // Width of the widest format number string, e.g. "10." -> 3.
209 size_t FormatNumWidth =
210 std::to_string(val: Formats.size()).size() + 1; // +1 for '.'
211 // Width of the widest analysis number string, e.g. "10.10." -> 6.
212 size_t AnalysisNumWidth = std::to_string(val: Formats.size()).size() + 1 +
213 std::to_string(val: MaxAnalysisCount).size() + 1;
214
215 // Where the format name starts (also where "Analyses:" is indented to).
216 size_t FormatNameCol = FormatIndent + FormatNumWidth + 1;
217 // Where the analysis number starts.
218 size_t AnalysisCol = FormatNameCol + AnalysisIndent;
219
220 return {
221 .FormatNumWidth: FormatNumWidth, .MaxFormatNameWidth: MaxFormatNameWidth, .FormatNameCol: FormatNameCol,
222 .AnalysisCol: AnalysisCol, .AnalysisNumWidth: AnalysisNumWidth, .MaxAnalysisNameWidth: MaxAnalysisNameWidth,
223 };
224}
225
226void listFormats() {
227 llvm::SmallVector<FormatData> Formats = collectFormats();
228 if (Formats.empty()) {
229 llvm::outs() << "No serialization formats registered.\n";
230 return;
231 }
232 printFormats(Formats, Layout: computePrintLayout(Formats));
233}
234
235//===----------------------------------------------------------------------===//
236// Input Validation
237//===----------------------------------------------------------------------===//
238
239struct FormatInput {
240 FormatFile InputFile;
241 std::optional<FormatFile> OutputFile;
242};
243
244FormatInput validateInput() {
245 assert(!ListFormats);
246
247 FormatInput FI;
248
249 // Validate the input path.
250 {
251 if (InputPath.empty()) {
252 fail(Msg: "no input file specified");
253 }
254
255 FI.InputFile = FormatFile::fromInputPath(Path: InputPath);
256 }
257
258 // Validate the output path.
259 if (!OutputPath.empty()) {
260 FI.OutputFile = FormatFile::fromOutputPath(Path: OutputPath);
261 }
262
263 return FI;
264}
265
266//===----------------------------------------------------------------------===//
267// Format Conversion
268//===----------------------------------------------------------------------===//
269
270template <typename ReadFn, typename WriteFn>
271void run(const FormatInput &FI, ReadFn Read, WriteFn Write) {
272 auto ExpectedResult = (FI.InputFile.Format->*Read)(FI.InputFile.Path);
273 if (!ExpectedResult) {
274 fail(ExpectedResult.takeError());
275 }
276
277 if (!FI.OutputFile) {
278 return;
279 }
280
281 auto Err =
282 (FI.OutputFile->Format->*Write)(*ExpectedResult, FI.OutputFile->Path);
283 if (Err) {
284 fail(std::move(Err));
285 }
286}
287
288void convert(const FormatInput &FI) {
289 switch (Type) {
290 case SummaryType::Auto:
291 if (UseEncoding) {
292 run(FI, Read: &SerializationFormat::readArtifactEncoding,
293 Write: &SerializationFormat::writeArtifactEncoding);
294 } else {
295 run(FI, Read: &SerializationFormat::readArtifact,
296 Write: &SerializationFormat::writeArtifact);
297 }
298 return;
299 case SummaryType::TU:
300 if (UseEncoding) {
301 run(FI, Read: &SerializationFormat::readTUSummaryEncoding,
302 Write: &SerializationFormat::writeTUSummaryEncoding);
303 } else {
304 run(FI, Read: &SerializationFormat::readTUSummary,
305 Write: &SerializationFormat::writeTUSummary);
306 }
307 return;
308 case SummaryType::LU:
309 if (UseEncoding) {
310 run(FI, Read: &SerializationFormat::readLUSummaryEncoding,
311 Write: &SerializationFormat::writeLUSummaryEncoding);
312 } else {
313 run(FI, Read: &SerializationFormat::readLUSummary,
314 Write: &SerializationFormat::writeLUSummary);
315 }
316 return;
317 case SummaryType::StaticLibrary:
318 // StaticLibrary has only an encoded representation, so --encoding is a
319 // no-op here: both paths route to readStaticLibrary / writeStaticLibrary.
320 run(FI, Read: &SerializationFormat::readStaticLibrary,
321 Write: &SerializationFormat::writeStaticLibrary);
322 return;
323 case SummaryType::MultiArchStaticLibrary:
324 // MultiArchStaticLibrary has only an encoded representation, so
325 // --encoding is a no-op here: both paths route to
326 // readMultiArchStaticLibrary / writeMultiArchStaticLibrary.
327 run(FI, Read: &SerializationFormat::readMultiArchStaticLibrary,
328 Write: &SerializationFormat::writeMultiArchStaticLibrary);
329 return;
330 case SummaryType::MultiArchSharedLibrary:
331 // MultiArchSharedLibrary has only an encoded representation, so
332 // --encoding is a no-op here: both paths route to
333 // readMultiArchSharedLibrary / writeMultiArchSharedLibrary.
334 run(FI, Read: &SerializationFormat::readMultiArchSharedLibrary,
335 Write: &SerializationFormat::writeMultiArchSharedLibrary);
336 return;
337 case SummaryType::WPA:
338 run(FI, Read: &SerializationFormat::readWPASuite,
339 Write: &SerializationFormat::writeWPASuite);
340 return;
341 }
342
343 llvm_unreachable("Unhandled SummaryType variant");
344}
345
346} // namespace
347
348//===----------------------------------------------------------------------===//
349// Driver
350//===----------------------------------------------------------------------===//
351
352int main(int argc, const char **argv) {
353 llvm::StringRef ToolHeading = "SSAF Format";
354
355 InitLLVM X(argc, argv);
356 initTool(argc, argv, Version: "0.1", Category&: SsafFormatCategory, ToolHeading);
357
358 loadPlugins(Paths: LoadPlugins);
359
360 if (ListFormats) {
361 listFormats();
362 } else {
363 FormatInput FI = validateInput();
364 convert(FI);
365 }
366
367 return 0;
368}
369