1 | //===--- tools/pp-trace/PPTrace.cpp - Clang preprocessor tracer -----------===// |
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 pp-trace, a tool for displaying a textual trace |
10 | // of the Clang preprocessor activity. It's based on a derivation of the |
11 | // PPCallbacks class, that once registerd with Clang, receives callback calls |
12 | // to its virtual members, and outputs the information passed to the callbacks |
13 | // in a high-level YAML format. |
14 | // |
15 | // The pp-trace tool also serves as the basis for a test of the PPCallbacks |
16 | // mechanism. |
17 | // |
18 | // The pp-trace tool supports the following general command line format: |
19 | // |
20 | // pp-trace [options] file... [-- compiler options] |
21 | // |
22 | // Basically you put the pp-trace options first, then the source file or files, |
23 | // and then -- followed by any options you want to pass to the compiler. |
24 | // |
25 | //===----------------------------------------------------------------------===// |
26 | |
27 | #include "PPCallbacksTracker.h" |
28 | #include "clang/AST/ASTConsumer.h" |
29 | #include "clang/AST/ASTContext.h" |
30 | #include "clang/Basic/SourceManager.h" |
31 | #include "clang/Driver/Options.h" |
32 | #include "clang/Frontend/CompilerInstance.h" |
33 | #include "clang/Frontend/FrontendAction.h" |
34 | #include "clang/Frontend/FrontendActions.h" |
35 | #include "clang/Lex/Preprocessor.h" |
36 | #include "clang/Tooling/Execution.h" |
37 | #include "clang/Tooling/Tooling.h" |
38 | #include "llvm/Option/Arg.h" |
39 | #include "llvm/Option/ArgList.h" |
40 | #include "llvm/Option/OptTable.h" |
41 | #include "llvm/Option/Option.h" |
42 | #include "llvm/Support/CommandLine.h" |
43 | #include "llvm/Support/FileSystem.h" |
44 | #include "llvm/Support/GlobPattern.h" |
45 | #include "llvm/Support/InitLLVM.h" |
46 | #include "llvm/Support/Path.h" |
47 | #include "llvm/Support/ToolOutputFile.h" |
48 | #include "llvm/Support/WithColor.h" |
49 | #include <string> |
50 | #include <vector> |
51 | |
52 | using namespace llvm; |
53 | |
54 | namespace clang { |
55 | namespace pp_trace { |
56 | |
57 | static cl::OptionCategory Cat("pp-trace options" ); |
58 | |
59 | static cl::opt<std::string> Callbacks( |
60 | "callbacks" , cl::init(Val: "*" ), |
61 | cl::desc("Comma-separated list of globs describing the list of callbacks " |
62 | "to output. Globs are processed in order of appearance. Globs " |
63 | "with the '-' prefix remove callbacks from the set. e.g. " |
64 | "'*,-Macro*'." ), |
65 | cl::cat(Cat)); |
66 | |
67 | static cl::opt<std::string> OutputFileName( |
68 | "output" , cl::init(Val: "-" ), |
69 | cl::desc("Output trace to the given file name or '-' for stdout." ), |
70 | cl::cat(Cat)); |
71 | |
72 | [[noreturn]] static void error(Twine Message) { |
73 | WithColor::error() << Message << '\n'; |
74 | exit(status: 1); |
75 | } |
76 | |
77 | namespace { |
78 | |
79 | class PPTraceAction : public ASTFrontendAction { |
80 | public: |
81 | PPTraceAction(const FilterType &Filters, raw_ostream &OS) |
82 | : Filters(Filters), OS(OS) {} |
83 | |
84 | protected: |
85 | std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI, |
86 | StringRef InFile) override { |
87 | Preprocessor &PP = CI.getPreprocessor(); |
88 | PP.addPPCallbacks( |
89 | C: std::make_unique<PPCallbacksTracker>(args: Filters, args&: CallbackCalls, args&: PP)); |
90 | return std::make_unique<ASTConsumer>(); |
91 | } |
92 | |
93 | void EndSourceFileAction() override { |
94 | OS << "---\n" ; |
95 | for (const CallbackCall &Callback : CallbackCalls) { |
96 | OS << "- Callback: " << Callback.Name << "\n" ; |
97 | for (const Argument &Arg : Callback.Arguments) |
98 | OS << " " << Arg.Name << ": " << Arg.Value << "\n" ; |
99 | } |
100 | OS << "...\n" ; |
101 | |
102 | CallbackCalls.clear(); |
103 | } |
104 | |
105 | private: |
106 | const FilterType &Filters; |
107 | raw_ostream &OS; |
108 | std::vector<CallbackCall> CallbackCalls; |
109 | }; |
110 | |
111 | class PPTraceFrontendActionFactory : public tooling::FrontendActionFactory { |
112 | public: |
113 | PPTraceFrontendActionFactory(const FilterType &Filters, raw_ostream &OS) |
114 | : Filters(Filters), OS(OS) {} |
115 | |
116 | std::unique_ptr<FrontendAction> create() override { |
117 | return std::make_unique<PPTraceAction>(args: Filters, args&: OS); |
118 | } |
119 | |
120 | private: |
121 | const FilterType &Filters; |
122 | raw_ostream &OS; |
123 | }; |
124 | } // namespace |
125 | } // namespace pp_trace |
126 | } // namespace clang |
127 | |
128 | int main(int argc, const char **argv) { |
129 | using namespace clang::pp_trace; |
130 | InitLLVM X(argc, argv); |
131 | auto OptionsParser = clang::tooling::CommonOptionsParser::create( |
132 | argc, argv, Category&: Cat, OccurrencesFlag: llvm::cl::ZeroOrMore); |
133 | if (!OptionsParser) |
134 | error(Message: toString(E: OptionsParser.takeError())); |
135 | // Parse the IgnoreCallbacks list into strings. |
136 | SmallVector<StringRef, 32> Patterns; |
137 | FilterType Filters; |
138 | StringRef(Callbacks).split(A&: Patterns, Separator: "," , |
139 | /*MaxSplit=*/-1, /*KeepEmpty=*/false); |
140 | for (StringRef Pattern : Patterns) { |
141 | Pattern = Pattern.trim(); |
142 | bool Enabled = !Pattern.consume_front(Prefix: "-" ); |
143 | Expected<GlobPattern> Pat = GlobPattern::create(Pat: Pattern); |
144 | if (Pat) |
145 | Filters.emplace_back(args: std::move(*Pat), args&: Enabled); |
146 | else |
147 | error(Message: toString(E: Pat.takeError())); |
148 | } |
149 | |
150 | // Create the tool and run the compilation. |
151 | clang::tooling::ClangTool Tool(OptionsParser->getCompilations(), |
152 | OptionsParser->getSourcePathList()); |
153 | |
154 | std::error_code EC; |
155 | llvm::ToolOutputFile Out(OutputFileName, EC, llvm::sys::fs::OF_TextWithCRLF); |
156 | if (EC) |
157 | error(Message: EC.message()); |
158 | PPTraceFrontendActionFactory Factory(Filters, Out.os()); |
159 | int HadErrors = Tool.run(Action: &Factory); |
160 | |
161 | // If we had errors, exit early. |
162 | if (HadErrors) |
163 | return HadErrors; |
164 | |
165 | Out.keep(); |
166 | |
167 | return 0; |
168 | } |
169 | |