Skip to content

Commit e679612

Browse files
committed
Swift: move most of TrapArena to TrapFile
1 parent e63d079 commit e679612

File tree

4 files changed

+138
-96
lines changed

4 files changed

+138
-96
lines changed

swift/extractor/SwiftExtractor.cpp

Lines changed: 9 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#include "swift/extractor/trap/TrapClasses.h"
1616
#include "swift/extractor/trap/TrapArena.h"
17+
#include "swift/extractor/trap/TrapOutput.h"
1718

1819
using namespace codeql;
1920

@@ -47,51 +48,16 @@ static void extractFile(const SwiftExtractorConfiguration& config, swift::Source
4748
return;
4849
}
4950

50-
// The extractor can be called several times from different processes with
51-
// the same input file(s)
52-
// We are using PID to avoid concurrent access
53-
// TODO: find a more robust approach to avoid collisions?
54-
std::string tempTrapName = file.getFilename().str() + '.' + std::to_string(getpid()) + ".trap";
55-
llvm::SmallString<PATH_MAX> tempTrapPath(config.trapDir);
56-
llvm::sys::path::append(tempTrapPath, tempTrapName);
51+
if (TrapOutput trap{config, file.getFilename().str()}) {
52+
TrapArena arena{};
5753

58-
llvm::StringRef trapParent = llvm::sys::path::parent_path(tempTrapPath);
59-
if (std::error_code ec = llvm::sys::fs::create_directories(trapParent)) {
60-
std::cerr << "Cannot create trap directory '" << trapParent.str() << "': " << ec.message()
61-
<< "\n";
62-
return;
63-
}
64-
65-
std::ofstream trap(tempTrapPath.str().str());
66-
if (!trap) {
67-
std::error_code ec;
68-
ec.assign(errno, std::generic_category());
69-
std::cerr << "Cannot create temp trap file '" << tempTrapPath.str().str()
70-
<< "': " << ec.message() << "\n";
71-
return;
72-
}
73-
trap << "// extractor-args: ";
74-
for (auto opt : config.frontendOptions) {
75-
trap << std::quoted(opt) << " ";
76-
}
77-
trap << "\n\n";
78-
79-
TrapArena arena{trap};
80-
// arena will be passed to another class in a later PR, the next block of code is only an example
81-
File f;
82-
f.id = arena.getLabel<FileTag>();
83-
f.name = srcFilePath.str().str();
84-
arena.emit(f);
85-
86-
// TODO: Pick a better name to avoid collisions
87-
std::string trapName = file.getFilename().str() + ".trap";
88-
llvm::SmallString<PATH_MAX> trapPath(config.trapDir);
89-
llvm::sys::path::append(trapPath, trapName);
54+
auto label = arena.allocateLabel<File::Tag>();
55+
trap.assignStar(label);
9056

91-
// TODO: The last process wins. Should we do better than that?
92-
if (std::error_code ec = llvm::sys::fs::rename(tempTrapPath, trapPath)) {
93-
std::cerr << "Cannot rename temp trap file '" << tempTrapPath.str().str() << "' -> '"
94-
<< trapPath.str().str() << "': " << ec.message() << "\n";
57+
File f{};
58+
f.id = label;
59+
f.name = srcFilePath.str().str();
60+
trap.emit(f);
9561
}
9662
}
9763

swift/extractor/trap/TrapArena.h

Lines changed: 4 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
//
2-
// Created by redsun82 on 18.01.22.
3-
//
4-
51
#pragma once
62

73
#include <iostream>
@@ -12,59 +8,16 @@
128

139
namespace codeql {
1410

15-
// TrapArena has the responsibilities to
16-
// * allocate distinct trap #-labels outputting their assignment to '*' or a @-keys to a trap output
17-
// * forwarding trap entries to said output
18-
// TODO: split or move these responsibilities into separate classes
11+
// TrapArena has the responsibilities to allocate distinct trap #-labels
12+
// TODO this is now a small functionality that will be moved to code upcoming from other PRs
1913
class TrapArena {
20-
public:
21-
explicit TrapArena(std::ostream& out) : out_{out} {}
22-
23-
// get a new label for *
24-
template <typename Tag>
25-
TrapLabel<Tag> getLabel() {
26-
auto ret = allocateLabel<Tag>();
27-
print(ret, "=*");
28-
return ret;
29-
}
30-
31-
// get a new label for an @-key
32-
template <typename Tag>
33-
TrapLabel<Tag> getLabel(const std::string& key) {
34-
auto ret = allocateLabel<Tag>();
35-
// prefix the key with the id to guarantee the same key is not used wrongly with different tags
36-
auto prefixed = std::string(Tag::prefix) + '_' + key;
37-
print(ret, "=@", quoted(prefixed));
38-
return ret;
39-
}
40-
41-
// same as getLabel(const std::string&) above, concatenating keyParts
42-
template <typename Tag, typename... Args>
43-
TrapLabel<Tag> getLabel(const Args&... keyParts) {
44-
std::ostringstream oss;
45-
(oss << ... << keyParts);
46-
return getLabel<Tag>(oss.str());
47-
}
48-
49-
// emit a trap entry
50-
template <typename Entry>
51-
void emit(const Entry& e) {
52-
print(e);
53-
}
54-
55-
private:
56-
template <typename... Args>
57-
void print(const Args&... args) {
58-
(out_ << ... << args) << '\n';
59-
}
14+
uint64_t id_{0};
6015

16+
public:
6117
template <typename Tag>
6218
TrapLabel<Tag> allocateLabel() {
6319
return {id_++};
6420
}
65-
66-
uint64_t id_{0};
67-
std::ostream& out_;
6821
};
6922

7023
} // namespace codeql

swift/extractor/trap/TrapLabel.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ class TrapLabel : public UntypedTrapLabel {
3434

3535
using UntypedTrapLabel::UntypedTrapLabel;
3636

37-
// we want only TrapArena to create non-default labels
37+
// we want one authority tasked with creating labels to avoid conflicts, having access to the
38+
// private constructor
39+
// this is the TrapArena class for the moment
3840
friend class TrapArena;
3941

4042
public:
@@ -59,8 +61,10 @@ inline auto trapQuoted(const std::string& s) {
5961
return std::quoted(s, '"', '"');
6062
}
6163

62-
template <typename Tag>
64+
template <typename TagParam>
6365
struct Binding {
66+
using Tag = TagParam;
67+
6468
TrapLabel<Tag> id;
6569
};
6670

swift/extractor/trap/TrapOutput.h

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
#pragma once
2+
3+
#include <memory>
4+
#include <llvm/ADT/SmallString.h>
5+
#include <llvm/Support/FileSystem.h>
6+
#include <llvm/Support/Path.h>
7+
8+
#include "swift/extractor/SwiftExtractorConfiguration.h"
9+
#include "swift/extractor/trap/TrapLabel.h"
10+
11+
namespace codeql {
12+
13+
// Sink for trap emissions and label assignments. This abstracts away `ofstream` operations
14+
// like `ofstream`, an explicit bool operator is provided, that return false if something
15+
// went wrong
16+
class TrapOutput {
17+
std::ofstream out_{};
18+
std::string target_;
19+
20+
public:
21+
// open a output stream. Internally a temporary file is created which is then moved into place
22+
// on `close()` or destruction
23+
TrapOutput(const SwiftExtractorConfiguration& config, const std::string& target) {
24+
// TODO: Pick a better name to avoid collisions
25+
llvm::SmallString<PATH_MAX> trapPath(config.trapDir);
26+
llvm::sys::path::append(trapPath, target);
27+
target_ = trapPath.str().str();
28+
29+
auto trapDir = llvm::sys::path::parent_path(trapPath);
30+
if (std::error_code ec = llvm::sys::fs::create_directories(trapDir)) {
31+
std::cerr << "Cannot create trap directory '" << trapDir.str() << "': " << ec.message()
32+
<< "\n";
33+
return;
34+
}
35+
36+
auto tempFile = getTempFile();
37+
out_ = std::ofstream{tempFile};
38+
if (!out_) {
39+
std::error_code ec;
40+
ec.assign(errno, std::generic_category());
41+
std::cerr << "Cannot create temp trap file '" << tempFile << "': " << ec.message() << "\n";
42+
return;
43+
}
44+
out_ << "// extractor-args: ";
45+
for (auto opt : config.frontendOptions) {
46+
out_ << std::quoted(opt) << " ";
47+
}
48+
out_ << "\n\n";
49+
}
50+
51+
~TrapOutput() { close(); }
52+
53+
TrapOutput(const TrapOutput& other) = delete;
54+
TrapOutput& operator=(const TrapOutput& other) = delete;
55+
56+
TrapOutput(TrapOutput&& other) : out_{std::move(other.out_)}, target_{std::move(other.target_)} {
57+
assert(!other.out_.is_open());
58+
}
59+
60+
TrapOutput& operator=(TrapOutput&& other) {
61+
close();
62+
out_ = std::move(other.out_);
63+
target_ = std::move(other.target_);
64+
return *this;
65+
}
66+
67+
explicit operator bool() const { return bool{out_}; }
68+
69+
void close() {
70+
// TODO: The last process wins. Should we do better than that?
71+
if (out_.is_open()) {
72+
out_.close();
73+
auto tempFile = getTempFile();
74+
auto targetFile = getTargetFile();
75+
if (std::error_code ec = llvm::sys::fs::rename(tempFile, targetFile)) {
76+
std::cerr << "Cannot rename temp trap file '" << tempFile << "' -> '" << targetFile
77+
<< "': " << ec.message() << "\n";
78+
}
79+
}
80+
}
81+
82+
template <typename Tag>
83+
void assignStar(TrapLabel<Tag> label) {
84+
print(label, "=*");
85+
}
86+
87+
template <typename Tag>
88+
void assignKey(TrapLabel<Tag> label, const std::string& key) {
89+
// prefix the key with the id to guarantee the same key is not used wrongly with different tags
90+
auto prefixed = std::string(Tag::prefix) + '_' + key;
91+
print(label, "=@", trapQuoted(prefixed));
92+
}
93+
94+
template <typename Tag, typename... Args>
95+
void assignKey(TrapLabel<Tag> label, const Args&... keyParts) {
96+
std::ostringstream oss;
97+
(oss << ... << keyParts);
98+
assignKey(label, oss.str());
99+
}
100+
101+
template <typename Entry>
102+
void emit(const Entry& e) {
103+
print(e);
104+
}
105+
106+
private:
107+
std::string getTargetFile() { return target_ + ".trap"; }
108+
std::string getTempFile() {
109+
// TODO: find a more robust approach to avoid collisions?
110+
return target_ + '.' + std::to_string(getpid()) + ".trap";
111+
}
112+
113+
template <typename... Args>
114+
void print(const Args&... args) {
115+
(out_ << ... << args) << '\n';
116+
}
117+
};
118+
119+
} // namespace codeql

0 commit comments

Comments
 (0)