1//===-- FindAllSymbolsMain.cpp - find all symbols tool ----------*- 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#include "FindAllSymbolsAction.h"
10#include "STLPostfixHeaderMap.h"
11#include "SymbolInfo.h"
12#include "SymbolReporter.h"
13#include "clang/ASTMatchers/ASTMatchFinder.h"
14#include "clang/ASTMatchers/ASTMatchers.h"
15#include "clang/Frontend/CompilerInstance.h"
16#include "clang/Frontend/FrontendActions.h"
17#include "clang/Lex/Preprocessor.h"
18#include "clang/Tooling/CommonOptionsParser.h"
19#include "clang/Tooling/Tooling.h"
20#include "llvm/ADT/ArrayRef.h"
21#include "llvm/ADT/SmallString.h"
22#include "llvm/ADT/StringRef.h"
23#include "llvm/Support/CommandLine.h"
24#include "llvm/Support/FileSystem.h"
25#include "llvm/Support/MemoryBuffer.h"
26#include "llvm/Support/Path.h"
27#include "llvm/Support/ThreadPool.h"
28#include "llvm/Support/raw_ostream.h"
29#include <map>
30#include <mutex>
31#include <set>
32#include <string>
33#include <system_error>
34#include <vector>
35
36using namespace clang::tooling;
37using namespace llvm;
38using SymbolInfo = clang::find_all_symbols::SymbolInfo;
39
40// Apply a custom category to all command-line options so that they are the
41// only ones displayed.
42static cl::OptionCategory FindAllSymbolsCategory("find_all_symbols options");
43
44// CommonOptionsParser declares HelpMessage with a description of the common
45// command-line options related to the compilation database and input files.
46// It's nice to have this help message in all tools.
47static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage);
48
49// A help message for this specific tool can be added afterwards.
50static cl::extrahelp MoreHelp("\nMore help text...");
51
52static cl::opt<std::string> OutputDir("output-dir", cl::desc(R"(
53The output directory for saving the results.)"),
54 cl::init(Val: "."),
55 cl::cat(FindAllSymbolsCategory));
56
57static cl::opt<std::string> MergeDir("merge-dir", cl::desc(R"(
58The directory for merging symbols.)"),
59 cl::init(Val: ""),
60 cl::cat(FindAllSymbolsCategory));
61namespace clang {
62namespace find_all_symbols {
63
64class YamlReporter : public SymbolReporter {
65public:
66 void reportSymbols(StringRef FileName,
67 const SymbolInfo::SignalMap &Symbols) override {
68 int FD;
69 SmallString<128> ResultPath;
70 llvm::sys::fs::createUniqueFile(
71 Model: OutputDir + "/" + llvm::sys::path::filename(path: FileName) + "-%%%%%%.yaml",
72 ResultFD&: FD, ResultPath);
73 llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
74 WriteSymbolInfosToStream(OS, Symbols);
75 }
76};
77
78bool Merge(llvm::StringRef MergeDir, llvm::StringRef OutputFile) {
79 std::error_code EC;
80 SymbolInfo::SignalMap Symbols;
81 std::mutex SymbolMutex;
82 auto AddSymbols = [&](ArrayRef<SymbolAndSignals> NewSymbols) {
83 // Synchronize set accesses.
84 std::unique_lock<std::mutex> LockGuard(SymbolMutex);
85 for (const auto &Symbol : NewSymbols) {
86 Symbols[Symbol.Symbol] += Symbol.Signals;
87 }
88 };
89
90 // Load all symbol files in MergeDir.
91 {
92 llvm::DefaultThreadPool Pool;
93 for (llvm::sys::fs::directory_iterator Dir(MergeDir, EC), DirEnd;
94 Dir != DirEnd && !EC; Dir.increment(ec&: EC)) {
95 // Parse YAML files in parallel.
96 Pool.async(
97 F: [&AddSymbols](std::string Path) {
98 auto Buffer = llvm::MemoryBuffer::getFile(Filename: Path);
99 if (!Buffer) {
100 llvm::errs() << "Can't open " << Path << "\n";
101 return;
102 }
103 std::vector<SymbolAndSignals> Symbols =
104 ReadSymbolInfosFromYAML(Yaml: Buffer.get()->getBuffer());
105 for (auto &Symbol : Symbols) {
106 // Only count one occurrence per file, to avoid spam.
107 Symbol.Signals.Seen = std::min(a: Symbol.Signals.Seen, b: 1u);
108 Symbol.Signals.Used = std::min(a: Symbol.Signals.Used, b: 1u);
109 }
110 // FIXME: Merge without creating such a heavy contention point.
111 AddSymbols(Symbols);
112 },
113 ArgList: Dir->path());
114 }
115 }
116
117 llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_None);
118 if (EC) {
119 llvm::errs() << "Can't open '" << OutputFile << "': " << EC.message()
120 << '\n';
121 return false;
122 }
123 WriteSymbolInfosToStream(OS, Symbols);
124 return true;
125}
126
127} // namespace clang
128} // namespace find_all_symbols
129
130int main(int argc, const char **argv) {
131 auto ExpectedParser =
132 CommonOptionsParser::create(argc, argv, Category&: FindAllSymbolsCategory);
133 if (!ExpectedParser) {
134 llvm::errs() << ExpectedParser.takeError();
135 return 1;
136 }
137
138 CommonOptionsParser &OptionsParser = ExpectedParser.get();
139 ClangTool Tool(OptionsParser.getCompilations(),
140 OptionsParser.getSourcePathList());
141
142 std::vector<std::string> sources = OptionsParser.getSourcePathList();
143 if (sources.empty()) {
144 llvm::errs() << "Must specify at least one one source file.\n";
145 return 1;
146 }
147 if (!MergeDir.empty()) {
148 clang::find_all_symbols::Merge(MergeDir, OutputFile: sources[0]);
149 return 0;
150 }
151
152 clang::find_all_symbols::YamlReporter Reporter;
153
154 auto Factory =
155 std::make_unique<clang::find_all_symbols::FindAllSymbolsActionFactory>(
156 args: &Reporter, args: clang::find_all_symbols::getSTLPostfixHeaderMap());
157 return Tool.run(Action: Factory.get());
158}
159

source code of clang-tools-extra/clang-include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp