mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Firstly, this change reworks how inter-process races are resolved. Moreover some responsability reorganization has led to merging `TrapArena` and `TrapOutput` again into a `TrapDomain` class. A `TargetFile` class is introduced, that is successfully created only for the first process that starts processing a given trap output file. From then on `TargetFile` simply wraps around `<<` stream operations, dumping them to a temporary file. When `TargetFile::commit` is called, the temporary file is moved on to the actual target trap file. Processes that lose the race can now just ignore the unneeded extraction and go on, while previously all processes would carry out all extractions overwriting each other at the end. Some of the file system logic contained in `SwiftExtractor.cpp` has been moved to this class, and two TODOs are solved: * introducing a better inter process file collision avoidance strategy * better error handling for trap output operations: if unable to write to the trap file (or carry out other basic file operations), we just abort. The changes to `ExprVisitor` and `StmtVisitor` are due to wanting to hide the raw `TrapDomain::createLabel` from them, and bring more funcionality under the generic caching/dispatching mechanism.
74 lines
2.0 KiB
C++
74 lines
2.0 KiB
C++
#include "swift/extractor/infra/TargetFile.h"
|
|
|
|
#include <llvm/Support/FileSystem.h>
|
|
#include <llvm/Support/Path.h>
|
|
|
|
namespace codeql {
|
|
namespace {
|
|
[[noreturn]] void error(const char* action, const std::string& arg, std::error_code ec) {
|
|
std::cerr << "Unable to " << action << ": " << arg << " (" << ec.message() << ")\n";
|
|
std::abort();
|
|
}
|
|
|
|
[[noreturn]] void error(const char* action, const std::string& arg) {
|
|
error(action, arg, {errno, std::system_category()});
|
|
}
|
|
|
|
void ensureParentDir(const std::string& path) {
|
|
auto parent = llvm::sys::path::parent_path(path);
|
|
if (auto ec = llvm::sys::fs::create_directories(parent)) {
|
|
error("create directory", parent.str(), ec);
|
|
}
|
|
}
|
|
|
|
std::string initPath(std::string_view target, std::string_view dir) {
|
|
std::string ret{dir};
|
|
assert(!target.empty() && "target must be a non-empty path");
|
|
if (target[0] != '/') {
|
|
ret += '/';
|
|
}
|
|
ret.append(target);
|
|
ensureParentDir(ret);
|
|
return ret;
|
|
}
|
|
} // namespace
|
|
|
|
TargetFile::TargetFile(std::string_view target,
|
|
std::string_view targetDir,
|
|
std::string_view workingDir)
|
|
: workingPath{initPath(target, workingDir)}, targetPath{initPath(target, targetDir)} {
|
|
errno = 0;
|
|
// since C++17 "x" mode opens with O_EXCL (fails if file already exists)
|
|
if (auto f = std::fopen(targetPath.c_str(), "wx")) {
|
|
std::fclose(f);
|
|
out.open(workingPath);
|
|
checkOutput("open file for writing");
|
|
} else {
|
|
if (errno != EEXIST) {
|
|
error("open file for writing", targetPath);
|
|
}
|
|
// else we just lost the race, do nothing (good() will return false to signal this)
|
|
}
|
|
}
|
|
|
|
bool TargetFile::good() const {
|
|
return out && out.is_open();
|
|
}
|
|
|
|
void TargetFile::commit() {
|
|
assert(good());
|
|
out.close();
|
|
errno = 0;
|
|
if (std::rename(workingPath.c_str(), targetPath.c_str()) != 0) {
|
|
error("rename file", targetPath);
|
|
}
|
|
}
|
|
|
|
void TargetFile::checkOutput(const char* action) {
|
|
if (!out) {
|
|
error(action, workingPath);
|
|
}
|
|
}
|
|
|
|
} // namespace codeql
|