• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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("*"),
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("-"),
69     cl::desc("Output trace to the given file name or '-' for stdout."),
70     cl::cat(Cat));
71 
error(Twine Message)72 LLVM_ATTRIBUTE_NORETURN static void error(Twine Message) {
73   WithColor::error() << Message << '\n';
74   exit(1);
75 }
76 
77 namespace {
78 
79 class PPTraceAction : public ASTFrontendAction {
80 public:
PPTraceAction(const FilterType & Filters,raw_ostream & OS)81   PPTraceAction(const FilterType &Filters, raw_ostream &OS)
82       : Filters(Filters), OS(OS) {}
83 
84 protected:
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)85   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
86                                                  StringRef InFile) override {
87     Preprocessor &PP = CI.getPreprocessor();
88     PP.addPPCallbacks(
89         std::make_unique<PPCallbacksTracker>(Filters, CallbackCalls, PP));
90     return std::make_unique<ASTConsumer>();
91   }
92 
EndSourceFileAction()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:
PPTraceFrontendActionFactory(const FilterType & Filters,raw_ostream & OS)113   PPTraceFrontendActionFactory(const FilterType &Filters, raw_ostream &OS)
114       : Filters(Filters), OS(OS) {}
115 
create()116   std::unique_ptr<FrontendAction> create() override {
117     return std::make_unique<PPTraceAction>(Filters, OS);
118   }
119 
120 private:
121   const FilterType &Filters;
122   raw_ostream &OS;
123 };
124 } // namespace
125 } // namespace pp_trace
126 } // namespace clang
127 
main(int argc,const char ** argv)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, Cat, llvm::cl::ZeroOrMore);
133   if (!OptionsParser)
134     error(toString(OptionsParser.takeError()));
135   // Parse the IgnoreCallbacks list into strings.
136   SmallVector<StringRef, 32> Patterns;
137   FilterType Filters;
138   StringRef(Callbacks).split(Patterns, ",",
139                              /*MaxSplit=*/-1, /*KeepEmpty=*/false);
140   for (StringRef Pattern : Patterns) {
141     Pattern = Pattern.trim();
142     bool Enabled = !Pattern.consume_front("-");
143     Expected<GlobPattern> Pat = GlobPattern::create(Pattern);
144     if (Pat)
145       Filters.emplace_back(std::move(*Pat), Enabled);
146     else
147       error(toString(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_Text);
156   if (EC)
157     error(EC.message());
158   PPTraceFrontendActionFactory Factory(Filters, Out.os());
159   int HadErrors = Tool.run(&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