1 //===-- FindAllSymbolsMain.cpp - find all symbols tool ----------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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"
33 #include <system_error>
36 using namespace clang::tooling
;
38 using SymbolInfo
= clang::find_all_symbols::SymbolInfo
;
40 // Apply a custom category to all command-line options so that they are the
41 // only ones displayed.
42 static cl::OptionCategory
FindAllSymbolsCategory("find_all_symbols options");
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.
47 static cl::extrahelp
CommonHelp(CommonOptionsParser::HelpMessage
);
49 // A help message for this specific tool can be added afterwards.
50 static cl::extrahelp
MoreHelp("\nMore help text...");
52 static cl::opt
<std::string
> OutputDir("output-dir", cl::desc(R
"(
53 The output directory for saving the results.)"),
55 cl::cat(FindAllSymbolsCategory
));
57 static cl::opt
<std::string
> MergeDir("merge-dir", cl::desc(R
"(
58 The directory for merging symbols.)"),
60 cl::cat(FindAllSymbolsCategory
));
62 namespace find_all_symbols
{
64 class YamlReporter
: public SymbolReporter
{
66 void reportSymbols(StringRef FileName
,
67 const SymbolInfo::SignalMap
&Symbols
) override
{
69 SmallString
<128> ResultPath
;
70 llvm::sys::fs::createUniqueFile(
71 OutputDir
+ "/" + llvm::sys::path::filename(FileName
) + "-%%%%%%.yaml",
73 llvm::raw_fd_ostream
OS(FD
, /*shouldClose=*/true);
74 WriteSymbolInfosToStream(OS
, Symbols
);
78 bool Merge(llvm::StringRef MergeDir
, llvm::StringRef OutputFile
) {
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
;
90 // Load all symbol files in MergeDir.
92 llvm::DefaultThreadPool Pool
;
93 for (llvm::sys::fs::directory_iterator
Dir(MergeDir
, EC
), DirEnd
;
94 Dir
!= DirEnd
&& !EC
; Dir
.increment(EC
)) {
95 // Parse YAML files in parallel.
97 [&AddSymbols
](std::string Path
) {
98 auto Buffer
= llvm::MemoryBuffer::getFile(Path
);
100 llvm::errs() << "Can't open " << Path
<< "\n";
103 std::vector
<SymbolAndSignals
> Symbols
=
104 ReadSymbolInfosFromYAML(Buffer
.get()->getBuffer());
105 for (auto &Symbol
: Symbols
) {
106 // Only count one occurrence per file, to avoid spam.
107 Symbol
.Signals
.Seen
= std::min(Symbol
.Signals
.Seen
, 1u);
108 Symbol
.Signals
.Used
= std::min(Symbol
.Signals
.Used
, 1u);
110 // FIXME: Merge without creating such a heavy contention point.
117 llvm::raw_fd_ostream
OS(OutputFile
, EC
, llvm::sys::fs::OF_None
);
119 llvm::errs() << "Can't open '" << OutputFile
<< "': " << EC
.message()
123 WriteSymbolInfosToStream(OS
, Symbols
);
128 } // namespace find_all_symbols
130 int main(int argc
, const char **argv
) {
131 auto ExpectedParser
=
132 CommonOptionsParser::create(argc
, argv
, FindAllSymbolsCategory
);
133 if (!ExpectedParser
) {
134 llvm::errs() << ExpectedParser
.takeError();
138 CommonOptionsParser
&OptionsParser
= ExpectedParser
.get();
139 ClangTool
Tool(OptionsParser
.getCompilations(),
140 OptionsParser
.getSourcePathList());
142 std::vector
<std::string
> sources
= OptionsParser
.getSourcePathList();
143 if (sources
.empty()) {
144 llvm::errs() << "Must specify at least one one source file.\n";
147 if (!MergeDir
.empty()) {
148 clang::find_all_symbols::Merge(MergeDir
, sources
[0]);
152 clang::find_all_symbols::YamlReporter Reporter
;
155 std::make_unique
<clang::find_all_symbols::FindAllSymbolsActionFactory
>(
156 &Reporter
, clang::find_all_symbols::getSTLPostfixHeaderMap());
157 return Tool
.run(Factory
.get());