235 строки
7.8 KiB
C++
235 строки
7.8 KiB
C++
//===--- 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 (std::vector<Argument>::const_iterator 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::tool_output_file 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 tool_output_file that we want to keep the file.
|
|
if (HadErrors == 0)
|
|
Out.keep();
|
|
}
|
|
|
|
return HadErrors;
|
|
}
|