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
52using namespace llvm;
53
54namespace clang {
55namespace pp_trace {
56
57static cl::OptionCategory Cat("pp-trace options");
58
59static 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
67static 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
77namespace {
78
79class PPTraceAction : public ASTFrontendAction {
80public:
81 PPTraceAction(const FilterType &Filters, raw_ostream &OS)
82 : Filters(Filters), OS(OS) {}
83
84protected:
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
105private:
106 const FilterType &Filters;
107 raw_ostream &OS;
108 std::vector<CallbackCall> CallbackCalls;
109};
110
111class PPTraceFrontendActionFactory : public tooling::FrontendActionFactory {
112public:
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
120private:
121 const FilterType &Filters;
122 raw_ostream &OS;
123};
124} // namespace
125} // namespace pp_trace
126} // namespace clang
127
128int 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

source code of clang-tools-extra/pp-trace/PPTrace.cpp