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