//===- lib/Tooling/AllTUsExecution.cpp - Execute actions on all TUs. ------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/Tooling/AllTUsExecution.h"
#include "clang/Tooling/ToolExecutorPluginRegistry.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/Threading.h"
#include "llvm/Support/VirtualFileSystem.h"
namespace clang {
namespace tooling {
const char *AllTUsToolExecutor::ExecutorName = "AllTUsToolExecutor";
namespace {
llvm::Error make_string_error(const llvm::Twine &Message) {
return llvm::make_error<llvm::StringError>(Message,
llvm::inconvertibleErrorCode());
}
ArgumentsAdjuster getDefaultArgumentsAdjusters() {
return combineAdjusters(
getClangStripOutputAdjuster(),
combineAdjusters(getClangSyntaxOnlyAdjuster(),
getClangStripDependencyFileAdjuster()));
}
class ThreadSafeToolResults : public ToolResults {
public:
void addResult(StringRef Key, StringRef Value) override {
std::unique_lock<std::mutex> LockGuard(Mutex);
Results.addResult(Key, Value);
}
std::vector<std::pair<llvm::StringRef, llvm::StringRef>>
AllKVResults() override {
return Results.AllKVResults();
}
void forEachResult(llvm::function_ref<void(StringRef Key, StringRef Value)>
Callback) override {
Results.forEachResult(Callback);
}
private:
InMemoryToolResults Results;
std::mutex Mutex;
};
} // namespace
llvm::cl::opt<std::string>
Filter("filter",
llvm::cl::desc("Only process files that match this filter. "
"This flag only applies to all-TUs."),
llvm::cl::init(".*"));
AllTUsToolExecutor::AllTUsToolExecutor(
const CompilationDatabase &Compilations, unsigned ThreadCount,
std::shared_ptr<PCHContainerOperations> PCHContainerOps)
: Compilations(Compilations), Results(new ThreadSafeToolResults),
Context(Results.get()), ThreadCount(ThreadCount) {}
AllTUsToolExecutor::AllTUsToolExecutor(
CommonOptionsParser Options, unsigned ThreadCount,
std::shared_ptr<PCHContainerOperations> PCHContainerOps)
: OptionsParser(std::move(Options)),
Compilations(OptionsParser->getCompilations()),
Results(new ThreadSafeToolResults), Context(Results.get()),
ThreadCount(ThreadCount) {}
llvm::Error AllTUsToolExecutor::execute(
llvm::ArrayRef<
std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
Actions) {
if (Actions.empty())
return make_string_error("No action to execute.");
if (Actions.size() != 1)
return make_string_error(
"Only support executing exactly 1 action at this point.");
std::string ErrorMsg;
std::mutex TUMutex;
auto AppendError = [&](llvm::Twine Err) {
std::unique_lock<std::mutex> LockGuard(TUMutex);
ErrorMsg += Err.str();
};
auto Log = [&](llvm::Twine Msg) {
std::unique_lock<std::mutex> LockGuard(TUMutex);
llvm::errs() << Msg.str() << "\n";
};
std::vector<std::string> Files;
llvm::Regex RegexFilter(Filter);
for (const auto& File : Compilations.getAllFiles()) {
if (RegexFilter.match(File))
Files.push_back(File);
}
// Add a counter to track the progress.
const std::string TotalNumStr = std::to_string(Files.size());
unsigned Counter = 0;
auto Count = [&]() {
std::unique_lock<std::mutex> LockGuard(TUMutex);
return ++Counter;
};
auto &Action = Actions.front();
{
llvm::ThreadPool Pool(llvm::hardware_concurrency(ThreadCount));
for (std::string File : Files) {
Pool.async(
[&](std::string Path) {
Log("[" + std::to_string(Count()) + "/" + TotalNumStr +
"] Processing file " + Path);
// Each thread gets an indepent copy of a VFS to allow different
// concurrent working directories.
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
llvm::vfs::createPhysicalFileSystem().release();
ClangTool Tool(Compilations, {Path},
std::make_shared<PCHContainerOperations>(), FS);
Tool.appendArgumentsAdjuster(Action.second);
Tool.appendArgumentsAdjuster(getDefaultArgumentsAdjusters());
for (const auto &FileAndContent : OverlayFiles)
Tool.mapVirtualFile(FileAndContent.first(),
FileAndContent.second);
if (Tool.run(Action.first.get()))
AppendError(llvm::Twine("Failed to run action on ") + Path +
"\n");
},
File);
}
// Make sure all tasks have finished before resetting the working directory.
Pool.wait();
}
if (!ErrorMsg.empty())
return make_string_error(ErrorMsg);
return llvm::Error::success();
}
llvm::cl::opt<unsigned> ExecutorConcurrency(
"execute-concurrency",
llvm::cl::desc("The number of threads used to process all files in "
"parallel. Set to 0 for hardware concurrency. "
"This flag only applies to all-TUs."),
llvm::cl::init(0));
class AllTUsToolExecutorPlugin : public ToolExecutorPlugin {
public:
llvm::Expected<std::unique_ptr<ToolExecutor>>
create(CommonOptionsParser &OptionsParser) override {
if (OptionsParser.getSourcePathList().empty())
return make_string_error(
"[AllTUsToolExecutorPlugin] Please provide a directory/file path in "
"the compilation database.");
return std::make_unique<AllTUsToolExecutor>(std::move(OptionsParser),
ExecutorConcurrency);
}
};
static ToolExecutorPluginRegistry::Add<AllTUsToolExecutorPlugin>
X("all-TUs", "Runs FrontendActions on all TUs in the compilation database. "
"Tool results are stored in memory.");
// This anchor is used to force the linker to link in the generated object file
// and thus register the plugin.
volatile int AllTUsToolExecutorAnchorSource = 0;
} // end namespace tooling
} // end namespace clang