blob: 917949f5b3d54b77f188eb1436c35869239352b0 [file] [log] [blame]
John Thompson99794542013-10-31 12:23:32 +00001//===--- tools/pp-trace/PPTrace.cpp - Clang preprocessor tracer -----------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements pp-trace, a tool for displaying a textual trace
11// of the Clang preprocessor activity. It's based on a derivation of the
12// PPCallbacks class, that once registerd with Clang, receives callback calls
13// to its virtual members, and outputs the information passed to the callbacks
14// in a high-level YAML format.
15//
16// The pp-trace tool also serves as the basis for a test of the PPCallbacks
17// mechanism.
18//
19// The pp-trace tool supports the following general command line format:
20//
John Thompson17c4f7d2013-10-31 12:53:42 +000021// pp-trace [pp-trace options] (source file) [compiler options]
John Thompson99794542013-10-31 12:23:32 +000022//
23// Basically you put the pp-trace options first, then the source file or files,
24// and then any options you want to pass to the compiler.
25//
26// These are the pp-trace options:
27//
28// -ignore (callback list) Don't display output for a comma-separated
29// list of callbacks, i.e.:
30// -ignore "FileChanged,InclusionDirective"
31//
32// -output (file) Output trace to the given file in a YAML
33// format, e.g.:
34//
35// ---
36// - Callback: Name
37// Argument1: Value1
38// Argument2: Value2
39// (etc.)
40// ...
41//
42// Future Directions:
43//
44// 1. Add option opposite to "-ignore" that specifys a comma-separated option
45// list of callbacs. Perhaps "-only" or "-exclusive".
46//
47//===----------------------------------------------------------------------===//
48
49#include "PPCallbacksTracker.h"
50#include "clang/AST/ASTConsumer.h"
51#include "clang/AST/ASTContext.h"
52#include "clang/AST/RecursiveASTVisitor.h"
53#include "clang/Basic/SourceManager.h"
54#include "clang/Driver/Options.h"
55#include "clang/Frontend/CompilerInstance.h"
56#include "clang/Frontend/FrontendActions.h"
57#include "clang/Lex/Preprocessor.h"
58#include "clang/Tooling/CompilationDatabase.h"
59#include "clang/Tooling/Tooling.h"
John Thompson99794542013-10-31 12:23:32 +000060#include "llvm/Option/Arg.h"
61#include "llvm/Option/ArgList.h"
62#include "llvm/Option/OptTable.h"
63#include "llvm/Option/Option.h"
64#include "llvm/Support/CommandLine.h"
65#include "llvm/Support/FileSystem.h"
66#include "llvm/Support/MemoryBuffer.h"
67#include "llvm/Support/Path.h"
68#include "llvm/Support/ToolOutputFile.h"
69#include <algorithm>
70#include <fstream>
71#include <iterator>
72#include <string>
73#include <vector>
74
75using namespace clang;
76using namespace clang::driver;
77using namespace clang::driver::options;
78using namespace clang::tooling;
79using namespace llvm;
80using namespace llvm::opt;
81
82// Options:
83
84// Collect the source files.
85cl::list<std::string> SourcePaths(cl::Positional,
86 cl::desc("<source0> [... <sourceN>]"),
87 cl::OneOrMore);
88
89// Option to specify a list or one or more callback names to ignore.
90cl::opt<std::string> IgnoreCallbacks(
91 "ignore", cl::init(""),
92 cl::desc("Ignore callbacks, i.e. \"Callback1, Callback2...\"."));
93
94// Option to specify the trace output file name.
95cl::opt<std::string> OutputFileName(
96 "output", cl::init(""),
97 cl::desc("Output trace to the given file name or '-' for stdout."));
98
99// Collect all other arguments, which will be passed to the front end.
100cl::list<std::string>
101CC1Arguments(cl::ConsumeAfter,
102 cl::desc("<arguments to be passed to front end>..."));
103
104// Frontend action stuff:
105
106// Consumer is responsible for setting up the callbacks.
107class PPTraceConsumer : public ASTConsumer {
108public:
109 PPTraceConsumer(SmallSet<std::string, 4> &Ignore,
110 std::vector<CallbackCall> &CallbackCalls, Preprocessor &PP) {
111 // PP takes ownership.
112 PP.addPPCallbacks(new PPCallbacksTracker(Ignore, CallbackCalls, PP));
113 }
John Thompson99794542013-10-31 12:23:32 +0000114};
115
116class PPTraceAction : public SyntaxOnlyAction {
117public:
118 PPTraceAction(SmallSet<std::string, 4> &Ignore,
119 std::vector<CallbackCall> &CallbackCalls)
120 : Ignore(Ignore), CallbackCalls(CallbackCalls) {}
121
122protected:
David Blaikie680c4c82014-08-10 19:56:59 +0000123 std::unique_ptr<clang::ASTConsumer>
124 CreateASTConsumer(CompilerInstance &CI, StringRef InFile) override {
125 return llvm::make_unique<PPTraceConsumer>(Ignore, CallbackCalls,
126 CI.getPreprocessor());
John Thompson99794542013-10-31 12:23:32 +0000127 }
128
129private:
130 SmallSet<std::string, 4> &Ignore;
131 std::vector<CallbackCall> &CallbackCalls;
132};
133
134class PPTraceFrontendActionFactory : public FrontendActionFactory {
135public:
136 PPTraceFrontendActionFactory(SmallSet<std::string, 4> &Ignore,
137 std::vector<CallbackCall> &CallbackCalls)
138 : Ignore(Ignore), CallbackCalls(CallbackCalls) {}
139
140 virtual PPTraceAction *create() {
141 return new PPTraceAction(Ignore, CallbackCalls);
142 }
143
144private:
145 SmallSet<std::string, 4> &Ignore;
146 std::vector<CallbackCall> &CallbackCalls;
147};
148
149// Output the trace given its data structure and a stream.
150int outputPPTrace(std::vector<CallbackCall> &CallbackCalls,
151 llvm::raw_ostream &OS) {
152 // Mark start of document.
153 OS << "---\n";
154
155 for (std::vector<CallbackCall>::const_iterator I = CallbackCalls.begin(),
156 E = CallbackCalls.end();
157 I != E; ++I) {
158 const CallbackCall &Callback = *I;
159 OS << "- Callback: " << Callback.Name << "\n";
160
161 for (std::vector<Argument>::const_iterator AI = Callback.Arguments.begin(),
162 AE = Callback.Arguments.end();
163 AI != AE; ++AI) {
164 const Argument &Arg = *AI;
165 OS << " " << Arg.Name << ": " << Arg.Value << "\n";
166 }
167 }
168
169 // Mark end of document.
170 OS << "...\n";
171
172 return 0;
173}
174
175// Program entry point.
176int main(int Argc, const char **Argv) {
177
178 // Parse command line.
179 cl::ParseCommandLineOptions(Argc, Argv, "pp-trace.\n");
180
181 // Parse the IgnoreCallbacks list into strings.
182 SmallVector<StringRef, 32> IgnoreCallbacksStrings;
183 StringRef(IgnoreCallbacks).split(IgnoreCallbacksStrings, ",",
184 /*MaxSplit=*/ -1, /*KeepEmpty=*/false);
185 SmallSet<std::string, 4> Ignore;
186 for (SmallVector<StringRef, 32>::iterator I = IgnoreCallbacksStrings.begin(),
187 E = IgnoreCallbacksStrings.end();
188 I != E; ++I)
189 Ignore.insert(*I);
190
191 // Create the compilation database.
192 SmallString<256> PathBuf;
193 sys::fs::current_path(PathBuf);
Ahmed Charles6a2dc5c2014-03-09 09:24:40 +0000194 std::unique_ptr<CompilationDatabase> Compilations;
John Thompson99794542013-10-31 12:23:32 +0000195 Compilations.reset(
196 new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments));
197
198 // Store the callback trace information here.
199 std::vector<CallbackCall> CallbackCalls;
200
201 // Create the tool and run the compilation.
202 ClangTool Tool(*Compilations, SourcePaths);
Benjamin Kramer6e914242014-07-24 10:23:33 +0000203 PPTraceFrontendActionFactory Factory(Ignore, CallbackCalls);
204 int HadErrors = Tool.run(&Factory);
John Thompson99794542013-10-31 12:23:32 +0000205
206 // If we had errors, exit early.
207 if (HadErrors)
208 return HadErrors;
209
210 // Do the output.
211 if (!OutputFileName.size()) {
212 HadErrors = outputPPTrace(CallbackCalls, llvm::outs());
213 } else {
214 // Set up output file.
Rafael Espindolab14bd532014-08-25 18:17:00 +0000215 std::error_code EC;
216 llvm::tool_output_file Out(OutputFileName, EC, llvm::sys::fs::F_Text);
217 if (EC) {
John Thompson99794542013-10-31 12:23:32 +0000218 llvm::errs() << "pp-trace: error creating " << OutputFileName << ":"
Rafael Espindolab14bd532014-08-25 18:17:00 +0000219 << EC.message() << "\n";
John Thompson99794542013-10-31 12:23:32 +0000220 return 1;
221 }
222
223 HadErrors = outputPPTrace(CallbackCalls, Out.os());
224
225 // Tell tool_output_file that we want to keep the file.
226 if (HadErrors == 0)
227 Out.keep();
228 }
229
230 return HadErrors;
231}