Swift: collect TRAP files related to an extractor run

In order to do this a mutable `SwiftExtractorState` is introduced.
This commit is contained in:
Paolo Tranquilli
2023-01-18 09:11:57 +01:00
parent c31c515205
commit c7f13f1036
6 changed files with 47 additions and 29 deletions

View File

@@ -109,7 +109,7 @@ static llvm::SmallVector<swift::Decl*> getTopLevelDecls(swift::ModuleDecl& modul
} }
static std::unordered_set<swift::ModuleDecl*> extractDeclarations( static std::unordered_set<swift::ModuleDecl*> extractDeclarations(
const SwiftExtractorConfiguration& config, SwiftExtractorState& state,
swift::CompilerInstance& compiler, swift::CompilerInstance& compiler,
swift::ModuleDecl& module, swift::ModuleDecl& module,
swift::SourceFile* primaryFile = nullptr) { swift::SourceFile* primaryFile = nullptr) {
@@ -118,7 +118,7 @@ static std::unordered_set<swift::ModuleDecl*> extractDeclarations(
// The extractor can be called several times from different processes with // The extractor can be called several times from different processes with
// the same input file(s). Using `TargetFile` the first process will win, and the following // the same input file(s). Using `TargetFile` the first process will win, and the following
// will just skip the work // will just skip the work
auto trap = createTargetTrapDomain(config, filename); auto trap = createTargetTrapDomain(state, filename);
if (!trap) { if (!trap) {
// another process arrived first, nothing to do for us // another process arrived first, nothing to do for us
return {}; return {};
@@ -170,8 +170,7 @@ static std::vector<swift::ModuleDecl*> collectLoadedModules(swift::CompilerInsta
return ret; return ret;
} }
void codeql::extractSwiftFiles(const SwiftExtractorConfiguration& config, void codeql::extractSwiftFiles(SwiftExtractorState& state, swift::CompilerInstance& compiler) {
swift::CompilerInstance& compiler) {
auto inputFiles = collectInputFilenames(compiler); auto inputFiles = collectInputFilenames(compiler);
std::vector<swift::ModuleDecl*> todo = collectLoadedModules(compiler); std::vector<swift::ModuleDecl*> todo = collectLoadedModules(compiler);
std::unordered_set<swift::ModuleDecl*> seen{todo.begin(), todo.end()}; std::unordered_set<swift::ModuleDecl*> seen{todo.begin(), todo.end()};
@@ -190,11 +189,11 @@ void codeql::extractSwiftFiles(const SwiftExtractorConfiguration& config,
if (inputFiles.count(sourceFile->getFilename().str()) == 0) { if (inputFiles.count(sourceFile->getFilename().str()) == 0) {
continue; continue;
} }
archiveFile(config, *sourceFile); archiveFile(state.configuration, *sourceFile);
encounteredModules = extractDeclarations(config, compiler, *module, sourceFile); encounteredModules = extractDeclarations(state, compiler, *module, sourceFile);
} }
if (!isFromSourceFile) { if (!isFromSourceFile) {
encounteredModules = extractDeclarations(config, compiler, *module); encounteredModules = extractDeclarations(state, compiler, *module);
} }
for (auto encountered : encounteredModules) { for (auto encountered : encounteredModules) {
if (seen.count(encountered) == 0) { if (seen.count(encountered) == 0) {

View File

@@ -1,11 +1,10 @@
#pragma once #pragma once
#include "swift/extractor/config/SwiftExtractorConfiguration.h" #include "swift/extractor/config/SwiftExtractorState.h"
#include <swift/AST/SourceFile.h> #include <swift/AST/SourceFile.h>
#include <swift/Frontend/Frontend.h> #include <swift/Frontend/Frontend.h>
#include <memory> #include <memory>
namespace codeql { namespace codeql {
void extractSwiftFiles(const SwiftExtractorConfiguration& config, void extractSwiftFiles(SwiftExtractorState& state, swift::CompilerInstance& compiler);
swift::CompilerInstance& compiler);
} // namespace codeql } // namespace codeql

View File

@@ -1,13 +1,15 @@
#include "swift/extractor/TargetTrapDomain.h" #include "swift/extractor/TargetTrapDomain.h"
#include <iomanip> #include <iomanip>
namespace codeql { namespace codeql {
std::optional<TrapDomain> createTargetTrapDomain(const SwiftExtractorConfiguration& configuration, std::optional<TrapDomain> createTargetTrapDomain(SwiftExtractorState& state,
const std::filesystem::path& target) { const std::filesystem::path& target) {
auto trap = target; auto trap = target;
trap += ".trap"; trap += ".trap";
if (auto ret = TargetFile::create(trap, configuration.trapDir, configuration.getTempTrapDir())) { state.traps.push_back(trap.relative_path());
if (auto ret = TargetFile::create(trap, state.configuration.trapDir,
state.configuration.getTempTrapDir())) {
*ret << "/* extractor-args:\n"; *ret << "/* extractor-args:\n";
for (const auto& opt : configuration.frontendOptions) { for (const auto& opt : state.configuration.frontendOptions) {
*ret << " " << std::quoted(opt) << " \\\n"; *ret << " " << std::quoted(opt) << " \\\n";
} }
*ret << "\n*/\n"; *ret << "\n*/\n";

View File

@@ -1,11 +1,11 @@
#pragma once #pragma once
#include "swift/extractor/trap/TrapDomain.h" #include "swift/extractor/trap/TrapDomain.h"
#include "swift/extractor/config/SwiftExtractorConfiguration.h" #include "swift/extractor/config/SwiftExtractorState.h"
namespace codeql { namespace codeql {
std::optional<TrapDomain> createTargetTrapDomain(const SwiftExtractorConfiguration& configuration, std::optional<TrapDomain> createTargetTrapDomain(SwiftExtractorState& state,
const std::filesystem::path& target); const std::filesystem::path& target);
} // namespace codeql } // namespace codeql

View File

@@ -0,0 +1,19 @@
#pragma once
#include <vector>
#include <filesystem>
#include <swift/AST/Decl.h>
#include "swift/extractor/config/SwiftExtractorConfiguration.h"
namespace codeql {
struct SwiftExtractorState {
const SwiftExtractorConfiguration configuration;
// All the trap files related to this extraction. This may also include trap files generated in a
// previous run but that this run requested as well. Paths are relative to `configuration.trapDir`
std::vector<std::filesystem::path> traps;
};
} // namespace codeql

View File

@@ -21,12 +21,12 @@
using namespace std::string_literals; using namespace std::string_literals;
// must be called before processFrontendOptions modifies output paths // must be called before processFrontendOptions modifies output paths
static void lockOutputSwiftModuleTraps(const codeql::SwiftExtractorConfiguration& config, static void lockOutputSwiftModuleTraps(codeql::SwiftExtractorState& state,
const swift::FrontendOptions& options) { const swift::FrontendOptions& options) {
for (const auto& input : options.InputsAndOutputs.getAllInputs()) { for (const auto& input : options.InputsAndOutputs.getAllInputs()) {
if (const auto& module = input.getPrimarySpecificPaths().SupplementaryOutputs.ModuleOutputPath; if (const auto& module = input.getPrimarySpecificPaths().SupplementaryOutputs.ModuleOutputPath;
!module.empty()) { !module.empty()) {
if (auto target = codeql::createTargetTrapDomain(config, codeql::resolvePath(module))) { if (auto target = codeql::createTargetTrapDomain(state, codeql::resolvePath(module))) {
target->emit("// trap file deliberately empty\n" target->emit("// trap file deliberately empty\n"
"// this swiftmodule was created during the build, so its entities must have" "// this swiftmodule was created during the build, so its entities must have"
" been extracted directly from source files"); " been extracted directly from source files");
@@ -65,18 +65,18 @@ static void processFrontendOptions(swift::FrontendOptions& options) {
} }
} }
codeql::TrapDomain invocationTrapDomain(codeql::SwiftExtractorState& state);
// This is part of the swiftFrontendTool interface, we hook into the // This is part of the swiftFrontendTool interface, we hook into the
// compilation pipeline and extract files after the Swift frontend performed // compilation pipeline and extract files after the Swift frontend performed
// semantic analysis // semantic analysis
class Observer : public swift::FrontendObserver { class Observer : public swift::FrontendObserver {
public: public:
explicit Observer(const codeql::SwiftExtractorConfiguration& config, explicit Observer(const codeql::SwiftExtractorConfiguration& config) : state{config} {}
codeql::SwiftDiagnosticsConsumer& diagConsumer)
: config{config}, diagConsumer{diagConsumer} {}
void parsedArgs(swift::CompilerInvocation& invocation) override { void parsedArgs(swift::CompilerInvocation& invocation) override {
auto& options = invocation.getFrontendOptions(); auto& options = invocation.getFrontendOptions();
lockOutputSwiftModuleTraps(config, options); lockOutputSwiftModuleTraps(state, options);
processFrontendOptions(options); processFrontendOptions(options);
} }
@@ -85,12 +85,13 @@ class Observer : public swift::FrontendObserver {
} }
void performedSemanticAnalysis(swift::CompilerInstance& compiler) override { void performedSemanticAnalysis(swift::CompilerInstance& compiler) override {
codeql::extractSwiftFiles(config, compiler); codeql::extractSwiftFiles(state, compiler);
} }
private: private:
const codeql::SwiftExtractorConfiguration& config; codeql::SwiftExtractorState state;
codeql::SwiftDiagnosticsConsumer& diagConsumer; codeql::TrapDomain invocationTrap{invocationTrapDomain(state)};
codeql::SwiftDiagnosticsConsumer diagConsumer{invocationTrap};
}; };
static std::string getenv_or(const char* envvar, const std::string& def) { static std::string getenv_or(const char* envvar, const std::string& def) {
@@ -145,11 +146,11 @@ static void checkWhetherToRunUnderTool(int argc, char* const* argv) {
// Creates a target file that should store per-invocation info, e.g. compilation args, // Creates a target file that should store per-invocation info, e.g. compilation args,
// compilations, diagnostics, etc. // compilations, diagnostics, etc.
codeql::TrapDomain invocationTrapDomain(const codeql::SwiftExtractorConfiguration& configuration) { codeql::TrapDomain invocationTrapDomain(codeql::SwiftExtractorState& state) {
auto timestamp = std::chrono::system_clock::now().time_since_epoch().count(); auto timestamp = std::chrono::system_clock::now().time_since_epoch().count();
auto filename = std::to_string(timestamp) + '-' + std::to_string(getpid()); auto filename = std::to_string(timestamp) + '-' + std::to_string(getpid());
auto target = std::filesystem::path("invocations") / std::filesystem::path(filename); auto target = std::filesystem::path("invocations") / std::filesystem::path(filename);
auto maybeDomain = codeql::createTargetTrapDomain(configuration, target); auto maybeDomain = codeql::createTargetTrapDomain(state, target);
if (!maybeDomain) { if (!maybeDomain) {
std::cerr << "Cannot create invocation trap file: " << target << "\n"; std::cerr << "Cannot create invocation trap file: " << target << "\n";
abort(); abort();
@@ -183,9 +184,7 @@ int main(int argc, char** argv) {
auto openInterception = codeql::setupFileInterception(configuration); auto openInterception = codeql::setupFileInterception(configuration);
auto invocationDomain = invocationTrapDomain(configuration); Observer observer(configuration);
codeql::SwiftDiagnosticsConsumer diagConsumer(invocationDomain);
Observer observer(configuration, diagConsumer);
int frontend_rc = swift::performFrontend(configuration.frontendOptions, "swift-extractor", int frontend_rc = swift::performFrontend(configuration.frontendOptions, "swift-extractor",
(void*)main, &observer); (void*)main, &observer);